303 lines
6.2 KiB
C++
303 lines
6.2 KiB
C++
// Copyright 2013 Dolphin Emulator Project
|
|
// Licensed under GPLv2
|
|
// Refer to the license.txt file included.
|
|
|
|
#include "Common/Common.h"
|
|
#include "Core/HW/Memmap.h"
|
|
#include "VideoBackends/Software/BPMemLoader.h"
|
|
#include "VideoBackends/Software/CPMemLoader.h"
|
|
#include "VideoBackends/Software/DebugUtil.h"
|
|
#include "VideoBackends/Software/OpcodeDecoder.h"
|
|
#include "VideoBackends/Software/SWCommandProcessor.h"
|
|
#include "VideoBackends/Software/SWStatistics.h"
|
|
#include "VideoBackends/Software/SWVertexLoader.h"
|
|
#include "VideoBackends/Software/SWVideoConfig.h"
|
|
#include "VideoBackends/Software/XFMemLoader.h"
|
|
#include "VideoCommon/DataReader.h"
|
|
|
|
typedef void (*DecodingFunction)(u32);
|
|
|
|
namespace OpcodeDecoder
|
|
{
|
|
static DecodingFunction currentFunction = nullptr;
|
|
static u32 minCommandSize;
|
|
static u16 streamSize;
|
|
static u16 streamAddress;
|
|
static bool readOpcode;
|
|
static SWVertexLoader vertexLoader;
|
|
static bool inObjectStream;
|
|
static u8 lastPrimCmd;
|
|
|
|
|
|
void DoState(PointerWrap &p)
|
|
{
|
|
p.Do(minCommandSize);
|
|
// Not sure what is wrong with this. Something(s) in here is causing dolphin to crash/hang when loading states saved from another run of dolphin. Doesn't seem too important anyway...
|
|
//vertexLoader.DoState(p);
|
|
p.Do(readOpcode);
|
|
p.Do(inObjectStream);
|
|
p.Do(lastPrimCmd);
|
|
p.Do(streamSize);
|
|
p.Do(streamAddress);
|
|
if (p.GetMode() == PointerWrap::MODE_READ)
|
|
ResetDecoding();
|
|
}
|
|
|
|
void DecodePrimitiveStream(u32 iBufferSize)
|
|
{
|
|
u32 vertexSize = vertexLoader.GetVertexSize();
|
|
|
|
bool skipPrimitives = g_bSkipCurrentFrame ||
|
|
swstats.thisFrame.numDrawnObjects < g_SWVideoConfig.drawStart ||
|
|
swstats.thisFrame.numDrawnObjects >= g_SWVideoConfig.drawEnd;
|
|
|
|
if (skipPrimitives)
|
|
{
|
|
while (streamSize > 0 && iBufferSize >= vertexSize)
|
|
{
|
|
g_pVideoData += vertexSize;
|
|
iBufferSize -= vertexSize;
|
|
streamSize--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (streamSize > 0 && iBufferSize >= vertexSize)
|
|
{
|
|
vertexLoader.LoadVertex();
|
|
iBufferSize -= vertexSize;
|
|
streamSize--;
|
|
}
|
|
}
|
|
|
|
if (streamSize == 0)
|
|
{
|
|
// return to normal command processing
|
|
ResetDecoding();
|
|
}
|
|
}
|
|
|
|
void ReadXFData(u32 iBufferSize)
|
|
{
|
|
_assert_msg_(VIDEO, iBufferSize >= (u32)(streamSize * 4), "Underflow during standard opcode decoding");
|
|
|
|
u32 pData[16];
|
|
for (int i = 0; i < streamSize; i++)
|
|
pData[i] = DataReadU32();
|
|
SWLoadXFReg(streamSize, streamAddress, pData);
|
|
|
|
// return to normal command processing
|
|
ResetDecoding();
|
|
}
|
|
|
|
void ExecuteDisplayList(u32 addr, u32 count)
|
|
{
|
|
u8 *videoDataSave = g_pVideoData;
|
|
|
|
u8 *dlStart = Memory::GetPointer(addr);
|
|
|
|
g_pVideoData = dlStart;
|
|
|
|
while (OpcodeDecoder::CommandRunnable(count))
|
|
{
|
|
OpcodeDecoder::Run(count);
|
|
|
|
// if data was read by the opcode decoder then the video data pointer changed
|
|
u32 readCount = (u32)(g_pVideoData - dlStart);
|
|
dlStart = g_pVideoData;
|
|
|
|
_assert_msg_(VIDEO, count >= readCount, "Display list underrun");
|
|
|
|
count -= readCount;
|
|
}
|
|
|
|
g_pVideoData = videoDataSave;
|
|
}
|
|
|
|
void DecodeStandard(u32 bufferSize)
|
|
{
|
|
_assert_msg_(VIDEO, CommandRunnable(bufferSize), "Underflow during standard opcode decoding");
|
|
|
|
int Cmd = DataReadU8();
|
|
|
|
if (Cmd == GX_NOP)
|
|
return;
|
|
// Causes a SIGBUS error on Android
|
|
// XXX: Investigate
|
|
#ifndef ANDROID
|
|
// check if switching in or out of an object
|
|
// only used for debugging
|
|
if (inObjectStream && (Cmd & 0x87) != lastPrimCmd)
|
|
{
|
|
inObjectStream = false;
|
|
DebugUtil::OnObjectEnd();
|
|
}
|
|
if (Cmd & 0x80 && !inObjectStream)
|
|
{
|
|
inObjectStream = true;
|
|
lastPrimCmd = Cmd & 0x87;
|
|
DebugUtil::OnObjectBegin();
|
|
}
|
|
#endif
|
|
switch (Cmd)
|
|
{
|
|
case GX_NOP:
|
|
break;
|
|
|
|
case GX_LOAD_CP_REG: //0x08
|
|
{
|
|
u32 SubCmd = DataReadU8();
|
|
u32 Value = DataReadU32();
|
|
SWLoadCPReg(SubCmd, Value);
|
|
}
|
|
break;
|
|
|
|
case GX_LOAD_XF_REG:
|
|
{
|
|
u32 Cmd2 = DataReadU32();
|
|
streamSize = ((Cmd2 >> 16) & 15) + 1;
|
|
streamAddress = Cmd2 & 0xFFFF;
|
|
currentFunction = ReadXFData;
|
|
minCommandSize = streamSize * 4;
|
|
readOpcode = false;
|
|
}
|
|
break;
|
|
|
|
case GX_LOAD_INDX_A: //used for position matrices
|
|
SWLoadIndexedXF(DataReadU32(), 0xC);
|
|
break;
|
|
case GX_LOAD_INDX_B: //used for normal matrices
|
|
SWLoadIndexedXF(DataReadU32(), 0xD);
|
|
break;
|
|
case GX_LOAD_INDX_C: //used for postmatrices
|
|
SWLoadIndexedXF(DataReadU32(), 0xE);
|
|
break;
|
|
case GX_LOAD_INDX_D: //used for lights
|
|
SWLoadIndexedXF(DataReadU32(), 0xF);
|
|
break;
|
|
|
|
case GX_CMD_CALL_DL:
|
|
{
|
|
u32 dwAddr = DataReadU32();
|
|
u32 dwCount = DataReadU32();
|
|
ExecuteDisplayList(dwAddr, dwCount);
|
|
}
|
|
break;
|
|
|
|
case 0x44:
|
|
// zelda 4 swords calls it and checks the metrics registers after that
|
|
break;
|
|
|
|
case GX_CMD_INVL_VC:// Invalidate (vertex cache?)
|
|
DEBUG_LOG(VIDEO, "Invalidate (vertex cache?)");
|
|
break;
|
|
|
|
case GX_LOAD_BP_REG: //0x61
|
|
{
|
|
u32 cmd = DataReadU32();
|
|
SWLoadBPReg(cmd);
|
|
}
|
|
break;
|
|
|
|
// draw primitives
|
|
default:
|
|
if (Cmd & 0x80)
|
|
{
|
|
u8 vatIndex = Cmd & GX_VAT_MASK;
|
|
u8 primitiveType = (Cmd & GX_PRIMITIVE_MASK) >> GX_PRIMITIVE_SHIFT;
|
|
vertexLoader.SetFormat(vatIndex, primitiveType);
|
|
|
|
// switch to primitive processing
|
|
streamSize = DataReadU16();
|
|
currentFunction = DecodePrimitiveStream;
|
|
minCommandSize = vertexLoader.GetVertexSize();
|
|
readOpcode = false;
|
|
|
|
INCSTAT(swstats.thisFrame.numPrimatives);
|
|
DEBUG_LOG(VIDEO, "Draw begin");
|
|
}
|
|
else
|
|
{
|
|
PanicAlert("GFX: Unknown Opcode (0x%x).\n", Cmd);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void Init()
|
|
{
|
|
inObjectStream = false;
|
|
lastPrimCmd = 0;
|
|
ResetDecoding();
|
|
}
|
|
|
|
void ResetDecoding()
|
|
{
|
|
currentFunction = DecodeStandard;
|
|
minCommandSize = 1;
|
|
readOpcode = true;
|
|
}
|
|
|
|
bool CommandRunnable(u32 iBufferSize)
|
|
{
|
|
if (iBufferSize < minCommandSize)
|
|
return false;
|
|
|
|
if (readOpcode)
|
|
{
|
|
u8 Cmd = DataPeek8(0);
|
|
u32 minSize = 1;
|
|
|
|
switch (Cmd)
|
|
{
|
|
case GX_LOAD_CP_REG: //0x08
|
|
minSize = 6;
|
|
break;
|
|
|
|
case GX_LOAD_XF_REG:
|
|
minSize = 5;
|
|
break;
|
|
|
|
case GX_LOAD_INDX_A: //used for position matrices
|
|
minSize = 5;
|
|
break;
|
|
case GX_LOAD_INDX_B: //used for normal matrices
|
|
minSize = 5;
|
|
break;
|
|
case GX_LOAD_INDX_C: //used for postmatrices
|
|
minSize = 5;
|
|
break;
|
|
case GX_LOAD_INDX_D: //used for lights
|
|
minSize = 5;
|
|
break;
|
|
|
|
case GX_CMD_CALL_DL:
|
|
minSize = 9;
|
|
break;
|
|
|
|
case GX_LOAD_BP_REG: //0x61
|
|
minSize = 5;
|
|
break;
|
|
|
|
// draw primitives
|
|
default:
|
|
if (Cmd & 0x80)
|
|
minSize = 3;
|
|
break;
|
|
}
|
|
|
|
return (iBufferSize >= minSize);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Run(u32 iBufferSize)
|
|
{
|
|
currentFunction(iBufferSize);
|
|
}
|
|
|
|
}
|