2013-04-02 06:54:19 +00:00
|
|
|
#include <algorithm>
|
2013-08-08 06:27:29 +00:00
|
|
|
#include "native/base/mutex.h"
|
|
|
|
#include "native/base/timeutil.h"
|
2012-12-29 01:10:29 +00:00
|
|
|
#include "GeDisasm.h"
|
2012-12-28 20:58:00 +00:00
|
|
|
#include "GPUCommon.h"
|
2012-12-29 01:10:29 +00:00
|
|
|
#include "GPUState.h"
|
2013-02-04 04:31:46 +00:00
|
|
|
#include "ChunkFile.h"
|
2013-04-21 22:16:45 +00:00
|
|
|
#include "Core/Config.h"
|
2013-04-03 15:10:35 +00:00
|
|
|
#include "Core/CoreTiming.h"
|
2013-04-01 06:02:46 +00:00
|
|
|
#include "Core/MemMap.h"
|
2013-02-10 15:36:06 +00:00
|
|
|
#include "Core/Host.h"
|
2013-04-05 08:13:54 +00:00
|
|
|
#include "Core/Reporting.h"
|
2013-04-01 06:02:46 +00:00
|
|
|
#include "Core/HLE/sceKernelInterrupt.h"
|
2013-04-04 07:35:38 +00:00
|
|
|
#include "Core/HLE/sceKernelMemory.h"
|
|
|
|
#include "Core/HLE/sceKernelThread.h"
|
2013-04-01 06:02:46 +00:00
|
|
|
#include "Core/HLE/sceGe.h"
|
2012-12-28 20:58:00 +00:00
|
|
|
|
2013-04-05 06:19:28 +00:00
|
|
|
GPUCommon::GPUCommon() :
|
|
|
|
currentList(NULL),
|
|
|
|
isbreak(false),
|
2013-04-07 19:45:42 +00:00
|
|
|
drawCompleteTicks(0),
|
2013-04-09 07:56:04 +00:00
|
|
|
busyTicks(0),
|
2013-04-05 06:19:28 +00:00
|
|
|
dumpNextFrame_(false),
|
|
|
|
dumpThisFrame_(false),
|
|
|
|
interruptsEnabled_(true)
|
|
|
|
{
|
2013-04-15 20:57:54 +00:00
|
|
|
memset(dls, 0, sizeof(dls));
|
2013-04-06 09:28:49 +00:00
|
|
|
for (int i = 0; i < DisplayListMaxCount; ++i) {
|
2013-04-05 06:19:28 +00:00
|
|
|
dls[i].state = PSP_GE_DL_STATE_NONE;
|
2013-04-07 19:45:42 +00:00
|
|
|
dls[i].waitTicks = 0;
|
2013-04-06 09:28:49 +00:00
|
|
|
}
|
2013-04-05 06:19:28 +00:00
|
|
|
}
|
2012-12-28 20:58:00 +00:00
|
|
|
|
2013-04-05 06:19:28 +00:00
|
|
|
void GPUCommon::PopDLQueue() {
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-05 06:19:28 +00:00
|
|
|
if(!dlQueue.empty()) {
|
|
|
|
dlQueue.pop_front();
|
|
|
|
if(!dlQueue.empty()) {
|
|
|
|
bool running = currentList->state == PSP_GE_DL_STATE_RUNNING;
|
|
|
|
currentList = &dls[dlQueue.front()];
|
|
|
|
if (running)
|
|
|
|
currentList->state = PSP_GE_DL_STATE_RUNNING;
|
|
|
|
} else {
|
|
|
|
currentList = NULL;
|
|
|
|
}
|
|
|
|
}
|
2012-12-28 20:58:00 +00:00
|
|
|
}
|
|
|
|
|
2013-04-01 06:23:03 +00:00
|
|
|
u32 GPUCommon::DrawSync(int mode) {
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-01 06:42:56 +00:00
|
|
|
if (mode < 0 || mode > 1)
|
|
|
|
return SCE_KERNEL_ERROR_INVALID_MODE;
|
|
|
|
|
2013-04-04 06:31:01 +00:00
|
|
|
if (mode == 0) {
|
2013-04-06 05:38:19 +00:00
|
|
|
// TODO: What if dispatch / interrupts disabled?
|
2013-04-07 19:45:42 +00:00
|
|
|
if (drawCompleteTicks > CoreTiming::GetTicks()) {
|
2013-04-06 05:38:19 +00:00
|
|
|
__KernelWaitCurThread(WAITTYPE_GEDRAWSYNC, 1, 0, 0, false, "GeDrawSync");
|
|
|
|
} else {
|
|
|
|
for (int i = 0; i < DisplayListMaxCount; ++i) {
|
|
|
|
if (dls[i].state == PSP_GE_DL_STATE_COMPLETED) {
|
|
|
|
dls[i].state = PSP_GE_DL_STATE_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-04-04 06:31:01 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-06 05:38:19 +00:00
|
|
|
// If there's no current list, it must be complete.
|
|
|
|
DisplayList *top = NULL;
|
|
|
|
for (auto it = dlQueue.begin(), end = dlQueue.end(); it != end; ++it) {
|
|
|
|
if (dls[*it].state != PSP_GE_DL_STATE_COMPLETED) {
|
|
|
|
top = &dls[*it];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!top || top->state == PSP_GE_DL_STATE_COMPLETED)
|
2013-04-04 06:31:01 +00:00
|
|
|
return PSP_GE_LIST_COMPLETED;
|
|
|
|
|
|
|
|
if (currentList->pc == currentList->stall)
|
|
|
|
return PSP_GE_LIST_STALLING;
|
|
|
|
|
|
|
|
return PSP_GE_LIST_DRAWING;
|
2013-04-01 06:23:03 +00:00
|
|
|
}
|
|
|
|
|
2013-04-05 06:19:28 +00:00
|
|
|
void GPUCommon::CheckDrawSync()
|
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-05 06:19:28 +00:00
|
|
|
if (dlQueue.empty()) {
|
|
|
|
for (int i = 0; i < DisplayListMaxCount; ++i)
|
|
|
|
dls[i].state = PSP_GE_DL_STATE_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-01 06:23:03 +00:00
|
|
|
int GPUCommon::ListSync(int listid, int mode)
|
2012-12-28 20:58:00 +00:00
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-05 06:19:28 +00:00
|
|
|
if (listid < 0 || listid >= DisplayListMaxCount)
|
|
|
|
return SCE_KERNEL_ERROR_INVALID_ID;
|
|
|
|
|
2013-04-01 06:42:56 +00:00
|
|
|
if (mode < 0 || mode > 1)
|
|
|
|
return SCE_KERNEL_ERROR_INVALID_MODE;
|
|
|
|
|
2013-04-06 09:28:49 +00:00
|
|
|
DisplayList& dl = dls[listid];
|
2013-04-04 06:05:54 +00:00
|
|
|
if (mode == 1) {
|
2013-04-05 06:19:28 +00:00
|
|
|
switch (dl.state) {
|
2013-04-04 06:05:54 +00:00
|
|
|
case PSP_GE_DL_STATE_QUEUED:
|
2013-04-05 06:19:28 +00:00
|
|
|
if (dl.interrupted)
|
2013-04-04 07:35:38 +00:00
|
|
|
return PSP_GE_LIST_PAUSED;
|
2013-04-04 06:05:54 +00:00
|
|
|
return PSP_GE_LIST_QUEUED;
|
|
|
|
|
|
|
|
case PSP_GE_DL_STATE_RUNNING:
|
2013-04-05 06:19:28 +00:00
|
|
|
if (dl.pc == dl.stall)
|
2013-04-04 06:05:54 +00:00
|
|
|
return PSP_GE_LIST_STALLING;
|
|
|
|
return PSP_GE_LIST_DRAWING;
|
|
|
|
|
|
|
|
case PSP_GE_DL_STATE_COMPLETED:
|
|
|
|
return PSP_GE_LIST_COMPLETED;
|
|
|
|
|
|
|
|
case PSP_GE_DL_STATE_PAUSED:
|
|
|
|
return PSP_GE_LIST_PAUSED;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return SCE_KERNEL_ERROR_INVALID_ID;
|
2012-12-28 20:58:00 +00:00
|
|
|
}
|
|
|
|
}
|
2013-04-04 06:05:54 +00:00
|
|
|
|
2013-04-07 19:45:42 +00:00
|
|
|
if (dl.waitTicks > CoreTiming::GetTicks()) {
|
2013-04-06 09:28:49 +00:00
|
|
|
__KernelWaitCurThread(WAITTYPE_GELISTSYNC, listid, 0, 0, false, "GeListSync");
|
|
|
|
}
|
2013-04-04 06:05:54 +00:00
|
|
|
return PSP_GE_LIST_COMPLETED;
|
2012-12-28 20:58:00 +00:00
|
|
|
}
|
|
|
|
|
2012-12-22 06:50:35 +00:00
|
|
|
u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head)
|
2012-12-28 20:58:00 +00:00
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-04 07:04:24 +00:00
|
|
|
// TODO Check the stack values in missing arg and ajust the stack depth
|
|
|
|
|
|
|
|
// Check alignment
|
|
|
|
// TODO Check the context and stack alignement too
|
|
|
|
if (((listpc | stall) & 3) != 0)
|
|
|
|
return 0x80000103;
|
|
|
|
|
2013-04-05 06:19:28 +00:00
|
|
|
int id = -1;
|
|
|
|
bool oldCompatibility = true;
|
|
|
|
if (sceKernelGetCompiledSdkVersion() > 0x01FFFFFF) {
|
|
|
|
//numStacks = 0;
|
|
|
|
//stack = NULL;
|
|
|
|
oldCompatibility = false;
|
|
|
|
}
|
|
|
|
|
2013-04-07 23:47:29 +00:00
|
|
|
u64 currentTicks = CoreTiming::GetTicks();
|
2013-04-05 06:19:28 +00:00
|
|
|
for (int i = 0; i < DisplayListMaxCount; ++i)
|
|
|
|
{
|
|
|
|
if (dls[i].state != PSP_GE_DL_STATE_NONE && dls[i].state != PSP_GE_DL_STATE_COMPLETED) {
|
|
|
|
if (dls[i].pc == listpc && !oldCompatibility) {
|
|
|
|
ERROR_LOG(G3D, "sceGeListEnqueue: can't enqueue, list address %08X already used", listpc);
|
|
|
|
return 0x80000021;
|
|
|
|
}
|
|
|
|
//if(dls[i].stack == stack) {
|
|
|
|
// ERROR_LOG(G3D, "sceGeListEnqueue: can't enqueue, list stack %08X already used", context);
|
|
|
|
// return 0x80000021;
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
if (dls[i].state == PSP_GE_DL_STATE_NONE)
|
|
|
|
{
|
|
|
|
// Prefer a list that isn't used
|
|
|
|
id = i;
|
|
|
|
break;
|
|
|
|
}
|
2013-04-07 23:47:29 +00:00
|
|
|
if (id < 0 && dls[i].state == PSP_GE_DL_STATE_COMPLETED && dls[i].waitTicks < currentTicks)
|
2013-04-05 06:19:28 +00:00
|
|
|
{
|
|
|
|
id = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (id < 0)
|
|
|
|
{
|
2013-04-05 08:13:54 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "No DL ID available to enqueue");
|
2013-04-05 06:19:28 +00:00
|
|
|
for(auto it = dlQueue.begin(); it != dlQueue.end(); ++it) {
|
|
|
|
DisplayList &dl = dls[*it];
|
|
|
|
DEBUG_LOG(G3D, "DisplayList %d status %d pc %08x stall %08x", *it, dl.state, dl.pc, dl.stall);
|
|
|
|
}
|
|
|
|
return SCE_KERNEL_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
DisplayList &dl = dls[id];
|
|
|
|
dl.id = id;
|
2013-02-10 15:36:06 +00:00
|
|
|
dl.startpc = listpc & 0xFFFFFFF;
|
2012-12-28 20:58:00 +00:00
|
|
|
dl.pc = listpc & 0xFFFFFFF;
|
|
|
|
dl.stall = stall & 0xFFFFFFF;
|
2013-04-02 06:54:19 +00:00
|
|
|
dl.subIntrBase = std::max(subIntrBase, -1);
|
2013-04-01 06:28:35 +00:00
|
|
|
dl.stackptr = 0;
|
2013-04-02 06:54:19 +00:00
|
|
|
dl.signal = PSP_GE_SIGNAL_NONE;
|
2013-04-04 07:35:38 +00:00
|
|
|
dl.interrupted = false;
|
2013-04-07 19:45:42 +00:00
|
|
|
dl.waitTicks = (u64)-1;
|
2013-08-07 06:59:28 +00:00
|
|
|
dl.interruptsEnabled = interruptsEnabled_;
|
2013-04-05 06:19:28 +00:00
|
|
|
|
2013-04-04 07:04:24 +00:00
|
|
|
if (head) {
|
|
|
|
if (currentList) {
|
|
|
|
if (currentList->state != PSP_GE_DL_STATE_PAUSED)
|
|
|
|
return SCE_KERNEL_ERROR_INVALID_VALUE;
|
|
|
|
currentList->state = PSP_GE_DL_STATE_QUEUED;
|
|
|
|
}
|
|
|
|
|
|
|
|
dl.state = PSP_GE_DL_STATE_PAUSED;
|
2013-04-05 06:19:28 +00:00
|
|
|
|
|
|
|
currentList = &dl;
|
|
|
|
dlQueue.push_front(id);
|
2013-04-04 07:04:24 +00:00
|
|
|
} else if (currentList) {
|
|
|
|
dl.state = PSP_GE_DL_STATE_QUEUED;
|
2013-04-05 06:19:28 +00:00
|
|
|
dlQueue.push_back(id);
|
2013-04-04 07:04:24 +00:00
|
|
|
} else {
|
|
|
|
dl.state = PSP_GE_DL_STATE_RUNNING;
|
2013-04-05 06:19:28 +00:00
|
|
|
currentList = &dl;
|
|
|
|
dlQueue.push_front(id);
|
2013-04-04 07:04:24 +00:00
|
|
|
|
2013-04-07 19:45:42 +00:00
|
|
|
drawCompleteTicks = (u64)-1;
|
2013-04-06 05:38:19 +00:00
|
|
|
|
2013-04-04 07:04:24 +00:00
|
|
|
// TODO save context when starting the list if param is set
|
|
|
|
ProcessDLQueue();
|
|
|
|
}
|
|
|
|
|
2013-04-05 06:19:28 +00:00
|
|
|
return id;
|
2012-12-28 20:58:00 +00:00
|
|
|
}
|
|
|
|
|
2013-04-01 06:23:03 +00:00
|
|
|
u32 GPUCommon::DequeueList(int listid)
|
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-05 06:25:13 +00:00
|
|
|
if (listid < 0 || listid >= DisplayListMaxCount || dls[listid].state == PSP_GE_DL_STATE_NONE)
|
|
|
|
return SCE_KERNEL_ERROR_INVALID_ID;
|
|
|
|
|
|
|
|
if (dls[listid].state == PSP_GE_DL_STATE_RUNNING || dls[listid].state == PSP_GE_DL_STATE_PAUSED)
|
|
|
|
return 0x80000021;
|
|
|
|
|
|
|
|
dls[listid].state = PSP_GE_DL_STATE_NONE;
|
|
|
|
|
|
|
|
if (listid == dlQueue.front())
|
|
|
|
PopDLQueue();
|
|
|
|
else
|
|
|
|
dlQueue.remove(listid);
|
|
|
|
|
2013-04-07 19:45:42 +00:00
|
|
|
dls[listid].waitTicks = 0;
|
2013-04-06 09:28:49 +00:00
|
|
|
__KernelTriggerWait(WAITTYPE_GELISTSYNC, listid, 0, "GeListSync");
|
2013-04-05 06:25:13 +00:00
|
|
|
|
|
|
|
CheckDrawSync();
|
|
|
|
|
2013-04-01 06:23:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 GPUCommon::UpdateStall(int listid, u32 newstall)
|
2012-12-28 20:58:00 +00:00
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-05 06:19:28 +00:00
|
|
|
if (listid < 0 || listid >= DisplayListMaxCount || dls[listid].state == PSP_GE_DL_STATE_NONE)
|
2013-04-05 06:25:13 +00:00
|
|
|
return SCE_KERNEL_ERROR_INVALID_ID;
|
2013-04-05 06:19:28 +00:00
|
|
|
|
|
|
|
dls[listid].stall = newstall & 0xFFFFFFF;
|
2013-04-21 03:15:47 +00:00
|
|
|
|
|
|
|
if (dls[listid].signal == PSP_GE_SIGNAL_HANDLER_PAUSE)
|
|
|
|
dls[listid].signal = PSP_GE_SIGNAL_HANDLER_SUSPEND;
|
2012-12-28 20:58:00 +00:00
|
|
|
|
|
|
|
ProcessDLQueue();
|
2013-04-01 06:23:03 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 GPUCommon::Continue()
|
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-04 07:35:38 +00:00
|
|
|
if (!currentList)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (currentList->state == PSP_GE_DL_STATE_PAUSED)
|
|
|
|
{
|
|
|
|
if (!isbreak)
|
|
|
|
{
|
|
|
|
if (currentList->signal == PSP_GE_SIGNAL_HANDLER_PAUSE)
|
|
|
|
return 0x80000021;
|
|
|
|
|
|
|
|
currentList->state = PSP_GE_DL_STATE_RUNNING;
|
|
|
|
currentList->signal = PSP_GE_SIGNAL_NONE;
|
|
|
|
|
|
|
|
// TODO Restore context of DL is necessary
|
|
|
|
// TODO Restore BASE
|
2013-04-06 05:38:19 +00:00
|
|
|
|
|
|
|
// We have a list now, so it's not complete.
|
2013-04-07 19:45:42 +00:00
|
|
|
drawCompleteTicks = (u64)-1;
|
2013-04-04 07:35:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
currentList->state = PSP_GE_DL_STATE_QUEUED;
|
|
|
|
}
|
|
|
|
else if (currentList->state == PSP_GE_DL_STATE_RUNNING)
|
|
|
|
{
|
|
|
|
if (sceKernelGetCompiledSdkVersion() >= 0x02000000)
|
|
|
|
return 0x80000020;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (sceKernelGetCompiledSdkVersion() >= 0x02000000)
|
|
|
|
return 0x80000004;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ProcessDLQueue();
|
2013-04-01 06:23:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 GPUCommon::Break(int mode)
|
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-01 06:42:56 +00:00
|
|
|
if (mode < 0 || mode > 1)
|
|
|
|
return SCE_KERNEL_ERROR_INVALID_MODE;
|
|
|
|
|
2013-04-04 07:35:38 +00:00
|
|
|
if (!currentList)
|
|
|
|
return 0x80000020;
|
|
|
|
|
2013-04-05 06:19:28 +00:00
|
|
|
if (mode == 1)
|
2013-04-04 07:35:38 +00:00
|
|
|
{
|
2013-04-05 06:19:28 +00:00
|
|
|
// Clear the queue
|
2013-04-04 07:35:38 +00:00
|
|
|
dlQueue.clear();
|
2013-04-05 06:19:28 +00:00
|
|
|
for (int i = 0; i < DisplayListMaxCount; ++i)
|
|
|
|
{
|
|
|
|
dls[i].state = PSP_GE_DL_STATE_NONE;
|
|
|
|
dls[i].signal = PSP_GE_SIGNAL_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentList = NULL;
|
2013-04-04 07:35:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentList->state == PSP_GE_DL_STATE_NONE || currentList->state == PSP_GE_DL_STATE_COMPLETED)
|
|
|
|
{
|
|
|
|
if (sceKernelGetCompiledSdkVersion() >= 0x02000000)
|
|
|
|
return 0x80000004;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentList->state == PSP_GE_DL_STATE_PAUSED)
|
|
|
|
{
|
|
|
|
if (sceKernelGetCompiledSdkVersion() > 0x02000010)
|
|
|
|
{
|
|
|
|
if (currentList->signal == PSP_GE_SIGNAL_HANDLER_PAUSE)
|
|
|
|
{
|
2013-04-05 08:13:54 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "sceGeBreak: can't break signal-pausing list");
|
2013-04-04 07:35:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0x80000020;
|
|
|
|
}
|
|
|
|
return 0x80000021;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentList->state == PSP_GE_DL_STATE_QUEUED)
|
|
|
|
{
|
|
|
|
currentList->state = PSP_GE_DL_STATE_PAUSED;
|
|
|
|
return currentList->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO Save BASE
|
|
|
|
// TODO Adjust pc to be just before SIGNAL/END
|
|
|
|
|
|
|
|
// TODO: Is this right?
|
|
|
|
if (currentList->signal == PSP_GE_SIGNAL_SYNC)
|
|
|
|
currentList->pc += 8;
|
|
|
|
|
|
|
|
currentList->interrupted = true;
|
|
|
|
currentList->state = PSP_GE_DL_STATE_PAUSED;
|
|
|
|
currentList->signal = PSP_GE_SIGNAL_HANDLER_SUSPEND;
|
|
|
|
isbreak = true;
|
|
|
|
|
|
|
|
return currentList->id;
|
2012-12-29 01:10:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool GPUCommon::InterpretList(DisplayList &list)
|
|
|
|
{
|
2013-04-21 22:16:45 +00:00
|
|
|
// Initialized to avoid a race condition with bShowDebugStats changing.
|
|
|
|
double start = 0.0;
|
|
|
|
if (g_Config.bShowDebugStats)
|
|
|
|
{
|
|
|
|
time_update();
|
|
|
|
start = time_now_d();
|
|
|
|
}
|
|
|
|
|
2013-04-06 16:59:24 +00:00
|
|
|
// TODO: This has to be right... but it freezes right now?
|
|
|
|
//if (list.state == PSP_GE_DL_STATE_PAUSED)
|
|
|
|
// return false;
|
|
|
|
|
2012-12-29 01:10:29 +00:00
|
|
|
currentList = &list;
|
2013-01-29 16:07:36 +00:00
|
|
|
|
2013-02-02 22:47:35 +00:00
|
|
|
// I don't know if this is the correct place to zero this, but something
|
|
|
|
// need to do it. See Sol Trigger title screen.
|
2013-04-16 05:20:49 +00:00
|
|
|
// TODO: Maybe this is per list? Should a stalled list remember the old value?
|
2013-02-02 22:47:35 +00:00
|
|
|
gstate_c.offsetAddr = 0;
|
|
|
|
|
2013-01-29 16:07:36 +00:00
|
|
|
if (!Memory::IsValidAddress(list.pc)) {
|
2013-04-05 08:13:54 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "DL PC = %08x WTF!!!!", list.pc);
|
2013-01-29 16:07:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
2013-02-17 00:06:06 +00:00
|
|
|
#if defined(USING_QT_UI)
|
2013-04-05 08:13:54 +00:00
|
|
|
if(host->GpuStep())
|
|
|
|
{
|
|
|
|
host->SendGPUStart();
|
|
|
|
}
|
2013-02-17 00:06:06 +00:00
|
|
|
#endif
|
2013-01-29 16:07:36 +00:00
|
|
|
|
2013-04-03 15:10:35 +00:00
|
|
|
cycleLastPC = list.pc;
|
2013-04-28 21:23:30 +00:00
|
|
|
downcount = list.stall == 0 ? 0xFFFFFFF : (list.stall - list.pc) / 4;
|
2013-04-04 06:05:54 +00:00
|
|
|
list.state = PSP_GE_DL_STATE_RUNNING;
|
2013-04-04 07:35:38 +00:00
|
|
|
list.interrupted = false;
|
2013-04-03 15:10:35 +00:00
|
|
|
|
2013-04-28 21:56:38 +00:00
|
|
|
gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING;
|
|
|
|
|
2013-04-20 22:23:35 +00:00
|
|
|
const bool dumpThisFrame = dumpThisFrame_;
|
2013-04-28 21:23:30 +00:00
|
|
|
// TODO: Add check for displaylist debugger.
|
|
|
|
const bool useFastRunLoop = !dumpThisFrame;
|
2013-04-04 08:07:30 +00:00
|
|
|
while (gpuState == GPUSTATE_RUNNING)
|
2012-12-29 01:10:29 +00:00
|
|
|
{
|
|
|
|
if (list.pc == list.stall)
|
2013-04-04 08:07:30 +00:00
|
|
|
{
|
|
|
|
gpuState = GPUSTATE_STALL;
|
2013-04-28 21:23:30 +00:00
|
|
|
downcount = 0;
|
2013-04-04 08:07:30 +00:00
|
|
|
}
|
2013-02-17 00:06:06 +00:00
|
|
|
|
2013-04-28 21:23:30 +00:00
|
|
|
if (useFastRunLoop)
|
|
|
|
FastRunLoop(list);
|
|
|
|
else
|
|
|
|
SlowRunLoop(list);
|
|
|
|
|
|
|
|
downcount = list.stall == 0 ? 0xFFFFFFF : (list.stall - list.pc) / 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We haven't run the op at list.pc, so it shouldn't count.
|
|
|
|
if (cycleLastPC != list.pc)
|
|
|
|
UpdatePC(list.pc - 4, list.pc);
|
|
|
|
|
|
|
|
if (g_Config.bShowDebugStats)
|
|
|
|
{
|
|
|
|
time_update();
|
|
|
|
gpuStats.msProcessingDisplayLists += time_now_d() - start;
|
|
|
|
}
|
|
|
|
return gpuState == GPUSTATE_DONE || gpuState == GPUSTATE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUCommon::SlowRunLoop(DisplayList &list)
|
|
|
|
{
|
|
|
|
const bool dumpThisFrame = dumpThisFrame_;
|
|
|
|
while (downcount > 0)
|
|
|
|
{
|
|
|
|
u32 op = Memory::ReadUnchecked_U32(list.pc);
|
2013-02-17 00:06:06 +00:00
|
|
|
u32 cmd = op >> 24;
|
|
|
|
|
2013-02-10 15:36:06 +00:00
|
|
|
#if defined(USING_QT_UI)
|
2013-04-28 21:23:30 +00:00
|
|
|
if (host->GpuStep())
|
2013-02-17 17:25:43 +00:00
|
|
|
host->SendGPUWait(cmd, list.pc, &gstate);
|
2013-02-10 15:36:06 +00:00
|
|
|
#endif
|
2013-04-28 21:23:30 +00:00
|
|
|
|
2012-12-29 01:10:29 +00:00
|
|
|
u32 diff = op ^ gstate.cmdmem[cmd];
|
|
|
|
PreExecuteOp(op, diff);
|
2013-04-20 22:23:35 +00:00
|
|
|
if (dumpThisFrame) {
|
2012-12-29 01:10:29 +00:00
|
|
|
char temp[256];
|
2013-04-20 22:23:35 +00:00
|
|
|
u32 prev = Memory::ReadUnchecked_U32(list.pc - 4);
|
2012-12-29 01:10:29 +00:00
|
|
|
GeDisassembleOp(list.pc, op, prev, temp);
|
2013-03-04 21:15:39 +00:00
|
|
|
NOTICE_LOG(HLE, "%s", temp);
|
2012-12-29 01:10:29 +00:00
|
|
|
}
|
2013-04-04 08:07:30 +00:00
|
|
|
gstate.cmdmem[cmd] = op;
|
2013-04-03 15:10:35 +00:00
|
|
|
|
2013-04-28 21:23:30 +00:00
|
|
|
ExecuteOp(op, diff);
|
2013-04-03 15:10:35 +00:00
|
|
|
|
2013-04-28 21:23:30 +00:00
|
|
|
list.pc += 4;
|
|
|
|
--downcount;
|
2013-04-21 22:16:45 +00:00
|
|
|
}
|
2012-12-29 01:10:29 +00:00
|
|
|
}
|
|
|
|
|
2013-04-28 20:34:29 +00:00
|
|
|
// The newPC parameter is used for jumps, we don't count cycles between.
|
2013-08-08 06:59:32 +00:00
|
|
|
inline void GPUCommon::UpdatePC(u32 currentPC, u32 newPC) {
|
2013-04-07 23:55:48 +00:00
|
|
|
// Rough estimate, 2 CPU ticks (it's double the clock rate) per GPU instruction.
|
2013-04-28 20:34:29 +00:00
|
|
|
cyclesExecuted += 2 * (currentPC - cycleLastPC) / 4;
|
2013-05-31 17:40:16 +00:00
|
|
|
gpuStats.otherGPUCycles += 2 * (currentPC - cycleLastPC) / 4;
|
2013-04-28 20:34:29 +00:00
|
|
|
cycleLastPC = newPC == 0 ? currentPC : newPC;
|
2013-04-28 21:23:30 +00:00
|
|
|
|
2013-04-28 21:30:28 +00:00
|
|
|
// Exit the runloop and recalculate things. This isn't common.
|
|
|
|
downcount = 0;
|
2013-04-28 20:34:29 +00:00
|
|
|
}
|
|
|
|
|
2013-08-08 06:59:32 +00:00
|
|
|
void GPUCommon::ReapplyGfxState() {
|
|
|
|
ScheduleEvent(GPU_EVENT_REAPPLY_GFX_STATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUCommon::ReapplyGfxStateInternal() {
|
2013-08-04 22:15:50 +00:00
|
|
|
// ShaderManager_DirtyShader();
|
|
|
|
// The commands are embedded in the command memory so we can just reexecute the words. Convenient.
|
|
|
|
// To be safe we pass 0xFFFFFFF as the diff.
|
|
|
|
/*
|
|
|
|
ExecuteOp(gstate.cmdmem[GE_CMD_ALPHABLENDENABLE], 0xFFFFFFFF);
|
|
|
|
ExecuteOp(gstate.cmdmem[GE_CMD_ALPHATESTENABLE], 0xFFFFFFFF);
|
|
|
|
ExecuteOp(gstate.cmdmem[GE_CMD_BLENDMODE], 0xFFFFFFFF);
|
|
|
|
ExecuteOp(gstate.cmdmem[GE_CMD_ZTEST], 0xFFFFFFFF);
|
|
|
|
ExecuteOp(gstate.cmdmem[GE_CMD_ZTESTENABLE], 0xFFFFFFFF);
|
|
|
|
ExecuteOp(gstate.cmdmem[GE_CMD_CULL], 0xFFFFFFFF);
|
|
|
|
ExecuteOp(gstate.cmdmem[GE_CMD_CULLFACEENABLE], 0xFFFFFFFF);
|
|
|
|
ExecuteOp(gstate.cmdmem[GE_CMD_SCISSOR1], 0xFFFFFFFF);
|
|
|
|
ExecuteOp(gstate.cmdmem[GE_CMD_SCISSOR2], 0xFFFFFFFF);
|
|
|
|
*/
|
|
|
|
|
2013-08-08 06:59:32 +00:00
|
|
|
for (int i = GE_CMD_VERTEXTYPE; i < GE_CMD_BONEMATRIXNUMBER; i++) {
|
|
|
|
if (i != GE_CMD_ORIGIN) {
|
2013-08-04 22:15:50 +00:00
|
|
|
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
|
2013-08-08 06:59:32 +00:00
|
|
|
}
|
2013-08-04 22:15:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Can't write to bonematrixnumber here
|
|
|
|
|
2013-08-08 06:59:32 +00:00
|
|
|
for (int i = GE_CMD_MORPHWEIGHT0; i < GE_CMD_PATCHFACING; i++) {
|
2013-08-04 22:15:50 +00:00
|
|
|
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
// There are a few here in the middle that we shouldn't execute...
|
|
|
|
|
2013-08-08 06:59:32 +00:00
|
|
|
for (int i = GE_CMD_VIEWPORTX1; i < GE_CMD_TRANSFERSTART; i++) {
|
2013-08-04 22:15:50 +00:00
|
|
|
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: there's more...
|
|
|
|
}
|
|
|
|
|
2013-08-08 06:59:32 +00:00
|
|
|
inline void GPUCommon::UpdateState(GPUState state) {
|
2013-04-28 20:34:29 +00:00
|
|
|
gpuState = state;
|
2013-04-28 21:23:30 +00:00
|
|
|
if (state != GPUSTATE_RUNNING)
|
|
|
|
downcount = 0;
|
2013-04-03 15:10:35 +00:00
|
|
|
}
|
|
|
|
|
2013-08-08 06:59:32 +00:00
|
|
|
GPUEvent GPUCommon::GetNextEvent() {
|
|
|
|
lock_guard guard(eventsLock);
|
|
|
|
if (events.empty()) {
|
|
|
|
return GPU_EVENT_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
GPUEvent ev = events.front();
|
|
|
|
events.pop_front();
|
|
|
|
eventsCond.notify_one();
|
|
|
|
return ev;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUCommon::ScheduleEvent(GPUEvent ev) {
|
|
|
|
lock_guard guard(eventsLock);
|
|
|
|
events.push_back(ev);
|
|
|
|
eventsCond.notify_one();
|
|
|
|
|
|
|
|
if (!g_Config.bUseCPUThread) {
|
|
|
|
RunEventsUntil(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUCommon::RunEventsUntil(u64 globalticks) {
|
|
|
|
do {
|
|
|
|
for (GPUEvent ev = GetNextEvent(); ev.type != GPU_EVENT_INVALID; ev = GetNextEvent()) {
|
|
|
|
switch (ev.type) {
|
|
|
|
case GPU_EVENT_PROCESS_QUEUE:
|
|
|
|
ProcessDLQueueInternal();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GPU_EVENT_REAPPLY_GFX_STATE:
|
|
|
|
ReapplyGfxStateInternal();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ProcessEvent(ev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Quit the loop if the queue is drained and coreState has tripped.
|
|
|
|
if (coreState != CORE_RUNNING) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// coreState changes won't wake us, so recheck periodically.
|
|
|
|
eventsCond.wait_for(eventsLock, 1);
|
|
|
|
} while (CoreTiming::GetTicks() < globalticks);
|
|
|
|
}
|
|
|
|
|
2013-08-04 23:31:11 +00:00
|
|
|
int GPUCommon::GetNextListIndex() {
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-08-04 23:31:11 +00:00
|
|
|
auto iter = dlQueue.begin();
|
|
|
|
if (iter != dlQueue.end()) {
|
|
|
|
return *iter;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GPUCommon::ProcessDLQueue() {
|
2013-08-08 06:59:32 +00:00
|
|
|
ScheduleEvent(GPU_EVENT_PROCESS_QUEUE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUCommon::ProcessDLQueueInternal() {
|
2013-04-03 15:10:35 +00:00
|
|
|
startingTicks = CoreTiming::GetTicks();
|
|
|
|
cyclesExecuted = 0;
|
|
|
|
|
2013-08-04 23:31:11 +00:00
|
|
|
if (startingTicks < busyTicks) {
|
2013-04-09 07:56:04 +00:00
|
|
|
DEBUG_LOG(HLE, "Can't execute a list yet, still busy for %lld ticks", busyTicks - startingTicks);
|
2013-08-08 06:59:32 +00:00
|
|
|
return;
|
2013-04-09 07:56:04 +00:00
|
|
|
}
|
|
|
|
|
2013-08-04 23:31:11 +00:00
|
|
|
for (int listIndex = GetNextListIndex(); listIndex != -1; listIndex = GetNextListIndex()) {
|
|
|
|
DisplayList &l = dls[listIndex];
|
|
|
|
DEBUG_LOG(G3D, "Okay, starting DL execution at %08x - stall = %08x", l.pc, l.stall);
|
|
|
|
if (!InterpretList(l)) {
|
2013-08-08 06:59:32 +00:00
|
|
|
return;
|
2013-08-04 23:31:11 +00:00
|
|
|
} else {
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-08-04 23:31:11 +00:00
|
|
|
// At the end, we can remove it from the queue and continue.
|
|
|
|
dlQueue.erase(std::remove(dlQueue.begin(), dlQueue.end(), listIndex), dlQueue.end());
|
2012-12-29 01:10:29 +00:00
|
|
|
}
|
|
|
|
}
|
2013-08-04 23:31:11 +00:00
|
|
|
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-04 06:31:01 +00:00
|
|
|
currentList = NULL;
|
2013-04-06 05:38:19 +00:00
|
|
|
|
2013-04-07 19:45:42 +00:00
|
|
|
drawCompleteTicks = startingTicks + cyclesExecuted;
|
2013-04-09 07:56:04 +00:00
|
|
|
busyTicks = std::max(busyTicks, drawCompleteTicks);
|
2013-04-07 19:45:42 +00:00
|
|
|
__GeTriggerSync(WAITTYPE_GEDRAWSYNC, 1, drawCompleteTicks);
|
2012-12-29 01:10:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GPUCommon::PreExecuteOp(u32 op, u32 diff) {
|
|
|
|
// Nothing to do
|
2012-12-29 19:41:33 +00:00
|
|
|
}
|
|
|
|
|
2013-04-01 06:02:46 +00:00
|
|
|
void GPUCommon::ExecuteOp(u32 op, u32 diff) {
|
|
|
|
u32 cmd = op >> 24;
|
|
|
|
u32 data = op & 0xFFFFFF;
|
|
|
|
|
|
|
|
// Handle control and drawing commands here directly. The others we delegate.
|
|
|
|
switch (cmd) {
|
|
|
|
case GE_CMD_NOP:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GE_CMD_OFFSETADDR:
|
|
|
|
gstate_c.offsetAddr = data << 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GE_CMD_ORIGIN:
|
2013-08-08 06:27:29 +00:00
|
|
|
{
|
|
|
|
lock_guard guard(listLock);
|
|
|
|
gstate_c.offsetAddr = currentList->pc;
|
|
|
|
}
|
2013-04-01 06:02:46 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GE_CMD_JUMP:
|
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-01 06:02:46 +00:00
|
|
|
u32 target = gstate_c.getRelativeAddress(data);
|
|
|
|
if (Memory::IsValidAddress(target)) {
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdatePC(currentList->pc, target - 4);
|
2013-04-01 06:02:46 +00:00
|
|
|
currentList->pc = target - 4; // pc will be increased after we return, counteract that
|
|
|
|
} else {
|
2013-04-05 08:13:54 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "JUMP to illegal address %08x - ignoring! data=%06x", target, data);
|
2013-04-01 06:02:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GE_CMD_CALL:
|
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-01 06:02:46 +00:00
|
|
|
// Saint Seiya needs correct support for relative calls.
|
|
|
|
u32 retval = currentList->pc + 4;
|
|
|
|
u32 target = gstate_c.getRelativeAddress(data);
|
2013-04-16 06:02:54 +00:00
|
|
|
if (currentList->stackptr == ARRAY_SIZE(currentList->stack)) {
|
2013-04-05 08:13:54 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "CALL: Stack full!");
|
2013-04-01 06:02:46 +00:00
|
|
|
} else if (!Memory::IsValidAddress(target)) {
|
2013-04-05 08:13:54 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "CALL to illegal address %08x - ignoring! data=%06x", target, data);
|
2013-04-01 06:02:46 +00:00
|
|
|
} else {
|
2013-04-16 06:02:54 +00:00
|
|
|
auto &stackEntry = currentList->stack[currentList->stackptr++];
|
|
|
|
stackEntry.pc = retval;
|
|
|
|
stackEntry.offsetAddr = gstate_c.offsetAddr;
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdatePC(currentList->pc, target - 4);
|
2013-04-01 06:02:46 +00:00
|
|
|
currentList->pc = target - 4; // pc will be increased after we return, counteract that
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GE_CMD_RET:
|
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-16 06:02:54 +00:00
|
|
|
if (currentList->stackptr == 0) {
|
2013-04-05 08:13:54 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "RET: Stack empty!");
|
2013-04-01 06:02:46 +00:00
|
|
|
} else {
|
2013-04-16 06:02:54 +00:00
|
|
|
auto &stackEntry = currentList->stack[--currentList->stackptr];
|
|
|
|
gstate_c.offsetAddr = stackEntry.offsetAddr;
|
|
|
|
u32 target = (currentList->pc & 0xF0000000) | (stackEntry.pc & 0x0FFFFFFF);
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdatePC(currentList->pc, target - 4);
|
2013-04-01 06:02:46 +00:00
|
|
|
currentList->pc = target - 4;
|
|
|
|
if (!Memory::IsValidAddress(currentList->pc)) {
|
2013-04-05 08:13:54 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "Invalid DL PC %08x on return", currentList->pc);
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdateState(GPUSTATE_ERROR);
|
2013-04-01 06:02:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GE_CMD_SIGNAL:
|
|
|
|
case GE_CMD_FINISH:
|
2013-04-03 15:15:49 +00:00
|
|
|
// Processed in GE_END.
|
2013-04-01 06:02:46 +00:00
|
|
|
break;
|
|
|
|
|
2013-04-20 22:23:35 +00:00
|
|
|
case GE_CMD_END: {
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-20 22:23:35 +00:00
|
|
|
u32 prev = Memory::ReadUnchecked_U32(currentList->pc - 4);
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdatePC(currentList->pc);
|
2013-04-01 06:02:46 +00:00
|
|
|
switch (prev >> 24) {
|
|
|
|
case GE_CMD_SIGNAL:
|
|
|
|
{
|
|
|
|
// TODO: see http://code.google.com/p/jpcsp/source/detail?r=2935#
|
2013-04-04 07:35:38 +00:00
|
|
|
SignalBehavior behaviour = static_cast<SignalBehavior>((prev >> 16) & 0xFF);
|
2013-04-01 06:02:46 +00:00
|
|
|
int signal = prev & 0xFFFF;
|
|
|
|
int enddata = data & 0xFFFF;
|
2013-04-06 17:30:12 +00:00
|
|
|
bool trigger = true;
|
2013-04-02 06:54:19 +00:00
|
|
|
currentList->subIntrToken = signal;
|
|
|
|
|
2013-04-01 06:02:46 +00:00
|
|
|
switch (behaviour) {
|
2013-04-02 06:54:19 +00:00
|
|
|
case PSP_GE_SIGNAL_HANDLER_SUSPEND:
|
2013-04-05 05:13:58 +00:00
|
|
|
if (sceKernelGetCompiledSdkVersion() <= 0x02000010)
|
|
|
|
currentList->state = PSP_GE_DL_STATE_PAUSED;
|
2013-04-04 07:35:38 +00:00
|
|
|
currentList->signal = behaviour;
|
2013-06-27 15:48:29 +00:00
|
|
|
DEBUG_LOG(G3D, "Signal with Wait UNIMPLEMENTED! signal/end: %04x %04x", signal, enddata);
|
2013-04-01 06:02:46 +00:00
|
|
|
break;
|
2013-04-02 06:54:19 +00:00
|
|
|
case PSP_GE_SIGNAL_HANDLER_CONTINUE:
|
2013-04-04 07:35:38 +00:00
|
|
|
currentList->signal = behaviour;
|
2013-06-27 15:48:29 +00:00
|
|
|
DEBUG_LOG(G3D, "Signal without wait. signal/end: %04x %04x", signal, enddata);
|
2013-04-01 06:02:46 +00:00
|
|
|
break;
|
2013-04-02 06:54:19 +00:00
|
|
|
case PSP_GE_SIGNAL_HANDLER_PAUSE:
|
2013-04-06 16:59:24 +00:00
|
|
|
currentList->state = PSP_GE_DL_STATE_PAUSED;
|
|
|
|
currentList->signal = behaviour;
|
2013-04-06 15:21:10 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "Signal with Pause UNIMPLEMENTED! signal/end: %04x %04x", signal, enddata);
|
2013-04-01 06:02:46 +00:00
|
|
|
break;
|
2013-04-02 06:54:19 +00:00
|
|
|
case PSP_GE_SIGNAL_SYNC:
|
2013-04-06 17:05:09 +00:00
|
|
|
currentList->signal = behaviour;
|
|
|
|
DEBUG_LOG(G3D, "Signal with Sync. signal/end: %04x %04x", signal, enddata);
|
2013-04-02 06:54:19 +00:00
|
|
|
break;
|
|
|
|
case PSP_GE_SIGNAL_JUMP:
|
2013-04-06 17:30:12 +00:00
|
|
|
{
|
|
|
|
trigger = false;
|
|
|
|
currentList->signal = behaviour;
|
|
|
|
// pc will be increased after we return, counteract that.
|
|
|
|
u32 target = ((signal << 16) | enddata) - 4;
|
|
|
|
if (!Memory::IsValidAddress(target)) {
|
|
|
|
ERROR_LOG_REPORT(G3D, "Signal with Jump: bad address. signal/end: %04x %04x", signal, enddata);
|
|
|
|
} else {
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdatePC(currentList->pc, target);
|
2013-04-06 17:30:12 +00:00
|
|
|
currentList->pc = target;
|
|
|
|
DEBUG_LOG(G3D, "Signal with Jump. signal/end: %04x %04x", signal, enddata);
|
|
|
|
}
|
|
|
|
}
|
2013-04-01 06:02:46 +00:00
|
|
|
break;
|
2013-04-02 06:54:19 +00:00
|
|
|
case PSP_GE_SIGNAL_CALL:
|
2013-04-06 17:30:12 +00:00
|
|
|
{
|
|
|
|
trigger = false;
|
|
|
|
currentList->signal = behaviour;
|
|
|
|
// pc will be increased after we return, counteract that.
|
|
|
|
u32 target = ((signal << 16) | enddata) - 4;
|
2013-04-16 06:02:54 +00:00
|
|
|
if (currentList->stackptr == ARRAY_SIZE(currentList->stack)) {
|
2013-04-06 17:30:12 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "Signal with Call: stack full. signal/end: %04x %04x", signal, enddata);
|
|
|
|
} else if (!Memory::IsValidAddress(target)) {
|
|
|
|
ERROR_LOG_REPORT(G3D, "Signal with Call: bad address. signal/end: %04x %04x", signal, enddata);
|
|
|
|
} else {
|
|
|
|
// TODO: This might save/restore other state...
|
2013-04-16 06:02:54 +00:00
|
|
|
auto &stackEntry = currentList->stack[currentList->stackptr++];
|
|
|
|
stackEntry.pc = currentList->pc;
|
|
|
|
stackEntry.offsetAddr = gstate_c.offsetAddr;
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdatePC(currentList->pc, target);
|
2013-04-06 17:30:12 +00:00
|
|
|
currentList->pc = target;
|
|
|
|
DEBUG_LOG(G3D, "Signal with Call. signal/end: %04x %04x", signal, enddata);
|
|
|
|
}
|
|
|
|
}
|
2013-04-01 06:02:46 +00:00
|
|
|
break;
|
2013-04-02 06:54:19 +00:00
|
|
|
case PSP_GE_SIGNAL_RET:
|
2013-04-06 17:30:12 +00:00
|
|
|
{
|
|
|
|
trigger = false;
|
|
|
|
currentList->signal = behaviour;
|
2013-04-16 06:02:54 +00:00
|
|
|
if (currentList->stackptr == 0) {
|
2013-04-06 17:30:12 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "Signal with Return: stack empty. signal/end: %04x %04x", signal, enddata);
|
|
|
|
} else {
|
|
|
|
// TODO: This might save/restore other state...
|
2013-04-16 06:02:54 +00:00
|
|
|
auto &stackEntry = currentList->stack[--currentList->stackptr];
|
|
|
|
gstate_c.offsetAddr = stackEntry.offsetAddr;
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdatePC(currentList->pc, stackEntry.pc);
|
2013-04-16 06:02:54 +00:00
|
|
|
currentList->pc = stackEntry.pc;
|
2013-04-06 17:30:12 +00:00
|
|
|
DEBUG_LOG(G3D, "Signal with Return. signal/end: %04x %04x", signal, enddata);
|
|
|
|
}
|
|
|
|
}
|
2013-04-01 06:02:46 +00:00
|
|
|
break;
|
|
|
|
default:
|
2013-04-05 08:13:54 +00:00
|
|
|
ERROR_LOG_REPORT(G3D, "UNKNOWN Signal UNIMPLEMENTED %i ! signal/end: %04x %04x", behaviour, signal, enddata);
|
2013-04-01 06:02:46 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-04-06 17:30:12 +00:00
|
|
|
// TODO: Technically, jump/call/ret should generate an interrupt, but before the pc change maybe?
|
2013-08-07 06:59:28 +00:00
|
|
|
if (currentList->interruptsEnabled && trigger) {
|
2013-04-07 19:45:42 +00:00
|
|
|
if (__GeTriggerInterrupt(currentList->id, currentList->pc, startingTicks + cyclesExecuted))
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdateState(GPUSTATE_INTERRUPT);
|
2013-04-05 07:21:47 +00:00
|
|
|
}
|
2013-04-01 06:02:46 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GE_CMD_FINISH:
|
2013-04-06 16:59:24 +00:00
|
|
|
switch (currentList->signal) {
|
|
|
|
case PSP_GE_SIGNAL_HANDLER_PAUSE:
|
2013-08-07 06:59:28 +00:00
|
|
|
if (currentList->interruptsEnabled) {
|
2013-04-07 19:45:42 +00:00
|
|
|
if (__GeTriggerInterrupt(currentList->id, currentList->pc, startingTicks + cyclesExecuted))
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdateState(GPUSTATE_INTERRUPT);
|
2013-04-06 16:59:24 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-04-06 17:05:09 +00:00
|
|
|
case PSP_GE_SIGNAL_SYNC:
|
|
|
|
currentList->signal = PSP_GE_SIGNAL_NONE;
|
|
|
|
// TODO: Technically this should still cause an interrupt. Probably for memory sync.
|
|
|
|
break;
|
|
|
|
|
2013-04-06 16:59:24 +00:00
|
|
|
default:
|
|
|
|
currentList->subIntrToken = prev & 0xFFFF;
|
|
|
|
currentList->state = PSP_GE_DL_STATE_COMPLETED;
|
2013-04-28 20:34:29 +00:00
|
|
|
UpdateState(GPUSTATE_DONE);
|
2013-08-07 06:59:28 +00:00
|
|
|
if (!currentList->interruptsEnabled || !__GeTriggerInterrupt(currentList->id, currentList->pc, startingTicks + cyclesExecuted)) {
|
2013-04-07 19:45:42 +00:00
|
|
|
currentList->waitTicks = startingTicks + cyclesExecuted;
|
2013-04-09 07:56:04 +00:00
|
|
|
busyTicks = std::max(busyTicks, currentList->waitTicks);
|
2013-04-07 19:45:42 +00:00
|
|
|
__GeTriggerSync(WAITTYPE_GELISTSYNC, currentList->id, currentList->waitTicks);
|
2013-04-06 16:59:24 +00:00
|
|
|
}
|
|
|
|
break;
|
2013-04-06 15:19:54 +00:00
|
|
|
}
|
2013-04-01 06:02:46 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DEBUG_LOG(G3D,"Ah, not finished: %06x", prev & 0xFFFFFF);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2013-04-20 22:23:35 +00:00
|
|
|
}
|
2013-04-01 06:02:46 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
DEBUG_LOG(G3D,"DL Unknown: %08x @ %08x", op, currentList == NULL ? 0 : currentList->pc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-29 19:41:33 +00:00
|
|
|
void GPUCommon::DoState(PointerWrap &p) {
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
|
|
|
|
2013-04-05 06:19:28 +00:00
|
|
|
p.Do<int>(dlQueue);
|
|
|
|
p.DoArray(dls, ARRAY_SIZE(dls));
|
|
|
|
int currentID = 0;
|
|
|
|
if (currentList != NULL) {
|
|
|
|
ptrdiff_t off = currentList - &dls[0];
|
2013-04-06 03:29:20 +00:00
|
|
|
currentID = (int) (off / sizeof(DisplayList));
|
2013-04-05 06:19:28 +00:00
|
|
|
}
|
2013-02-12 09:06:11 +00:00
|
|
|
p.Do(currentID);
|
|
|
|
if (currentID == 0) {
|
2013-04-05 06:19:28 +00:00
|
|
|
currentList = NULL;
|
2013-02-12 09:06:11 +00:00
|
|
|
} else {
|
2013-04-05 06:19:28 +00:00
|
|
|
currentList = &dls[currentID];
|
2013-02-12 09:06:11 +00:00
|
|
|
}
|
|
|
|
p.Do(interruptRunning);
|
2013-04-20 22:23:35 +00:00
|
|
|
u32 prev; // TODO: kill. just didn't want to break states right now...
|
2013-02-12 09:06:11 +00:00
|
|
|
p.Do(prev);
|
2013-04-04 08:07:30 +00:00
|
|
|
p.Do(gpuState);
|
2013-04-04 07:35:38 +00:00
|
|
|
p.Do(isbreak);
|
2013-04-07 19:45:42 +00:00
|
|
|
p.Do(drawCompleteTicks);
|
2013-04-09 09:18:09 +00:00
|
|
|
p.Do(busyTicks);
|
2012-12-29 19:41:33 +00:00
|
|
|
p.DoMarker("GPUCommon");
|
|
|
|
}
|
2013-02-03 23:41:16 +00:00
|
|
|
|
2013-04-06 15:19:54 +00:00
|
|
|
void GPUCommon::InterruptStart(int listid)
|
2013-02-03 23:41:16 +00:00
|
|
|
{
|
|
|
|
interruptRunning = true;
|
|
|
|
}
|
2013-04-06 15:19:54 +00:00
|
|
|
void GPUCommon::InterruptEnd(int listid)
|
2013-02-03 23:41:16 +00:00
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-02-03 23:41:16 +00:00
|
|
|
interruptRunning = false;
|
2013-04-04 07:35:38 +00:00
|
|
|
isbreak = false;
|
2013-04-06 15:19:54 +00:00
|
|
|
|
|
|
|
DisplayList &dl = dls[listid];
|
|
|
|
// TODO: Unless the signal handler could change it?
|
2013-04-07 23:47:29 +00:00
|
|
|
if (dl.state == PSP_GE_DL_STATE_COMPLETED || dl.state == PSP_GE_DL_STATE_NONE) {
|
2013-04-07 19:45:42 +00:00
|
|
|
dl.waitTicks = 0;
|
2013-04-06 15:19:54 +00:00
|
|
|
__KernelTriggerWait(WAITTYPE_GELISTSYNC, listid, 0, "GeListSync", true);
|
|
|
|
}
|
|
|
|
|
2013-04-21 03:15:47 +00:00
|
|
|
if (dl.signal == PSP_GE_SIGNAL_HANDLER_PAUSE)
|
|
|
|
dl.signal = PSP_GE_SIGNAL_HANDLER_SUSPEND;
|
|
|
|
|
2013-02-03 23:41:16 +00:00
|
|
|
ProcessDLQueue();
|
|
|
|
}
|
2013-04-07 19:45:42 +00:00
|
|
|
|
|
|
|
// TODO: Maybe cleaner to keep this in GE and trigger the clear directly?
|
|
|
|
void GPUCommon::SyncEnd(WaitType waitType, int listid, bool wokeThreads)
|
|
|
|
{
|
2013-08-08 06:27:29 +00:00
|
|
|
lock_guard guard(listLock);
|
2013-04-07 19:45:42 +00:00
|
|
|
if (waitType == WAITTYPE_GEDRAWSYNC && wokeThreads)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < DisplayListMaxCount; ++i) {
|
|
|
|
if (dls[i].state == PSP_GE_DL_STATE_COMPLETED) {
|
|
|
|
dls[i].state = PSP_GE_DL_STATE_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|