Merged 'FifoBusy' branch. Thanks
to marcosvitali. Added an external exception check when the CPU writes to the FIFO. This allows the CPU time to service FIFO overflows. Fixes random hangs caused by FIFO overflows and desyncs like in "The Last Story" and "Battalion Wars 2". Thanks to marcosvitali for the research. Added some code to unlink invalidated blocks so that the recompiled block can be linked (speed-up). This release still fixed the hangs produced by fifo overflow without sacrifice performance. For example you can test Tutorial moves at the beginning of The last history now is fluid 30/60. Fixed possibles random hangs in DC mode. Fixed hangs in DC mode in (Simpsons, Monkey Island, Pokemon XD, etc) Implemented accurate management of Pixel Engine Interrupts. Now the GPU loop is stopped when a PE Interrupt needs to be managed and resumed when Pixel Engine finish. Fixed Metroid Prime 3 and 2 desync. And other games with desync because of FIFO Reset. That happens because FIFO_RW_DISTANCE_HI must be written first, for checking fifo.CPReadWriteDistance == 0, so some fifo resets was not managed in the right way. Fixed Super Monkey Ball in some cases when the game write the WriteReadDistance need to be safe like the SafeCPRead. Improved the CheckException for the GatherPipe writes in JIT, now only the External Exceptions are processed. Fixed definitely Pokemon XD in dual core mode. This game is doing something not allowed. It attach to CPU the same fifo attached to the GPU in multibuffer mode. I added a check to prevent overwrite the GPU FIFO with the CPU FIFO. If the game do that on breakpoint the solution can fail. Fixed ReadWriteDistance calc when CPRead > CPWrite. Added Token and Finish cause to GP Jit checking. Additional cleanup in CommandProcessor. Fixes issue 5209 Fixes issue 5055 Fixes issue 4889 Fixes issue 4061 Fixes issue 4010 Fixes issue 3902
This commit is contained in:
commit
a60a0825a3
|
@ -18,6 +18,7 @@
|
|||
#include "Common.h"
|
||||
#include "DebugInterface.h"
|
||||
#include "BreakPoints.h"
|
||||
#include "../../Core/Src/PowerPC/JitCommon/JitBase.h"
|
||||
#include <sstream>
|
||||
|
||||
bool BreakPoints::IsAddressBreakPoint(u32 _iAddress)
|
||||
|
@ -70,7 +71,11 @@ void BreakPoints::AddFromStrings(const TBreakPointsStr& bps)
|
|||
void BreakPoints::Add(const TBreakPoint& bp)
|
||||
{
|
||||
if (!IsAddressBreakPoint(bp.iAddress))
|
||||
{
|
||||
m_BreakPoints.push_back(bp);
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Add(u32 em_address, bool temp)
|
||||
|
@ -83,21 +88,35 @@ void BreakPoints::Add(u32 em_address, bool temp)
|
|||
pt.iAddress = em_address;
|
||||
|
||||
m_BreakPoints.push_back(pt);
|
||||
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(em_address, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Remove(u32 _iAddress)
|
||||
void BreakPoints::Remove(u32 em_address)
|
||||
{
|
||||
for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i)
|
||||
{
|
||||
if (i->iAddress == _iAddress)
|
||||
if (i->iAddress == em_address)
|
||||
{
|
||||
m_BreakPoints.erase(i);
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(em_address, 4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Clear()
|
||||
{
|
||||
for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i)
|
||||
{
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(i->iAddress, 4);
|
||||
m_BreakPoints.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
MemChecks::TMemChecksStr MemChecks::GetStrings() const
|
||||
{
|
||||
|
|
|
@ -78,7 +78,7 @@ public:
|
|||
|
||||
// Remove Breakpoint
|
||||
void Remove(u32 _iAddress);
|
||||
void Clear() { m_BreakPoints.clear(); };
|
||||
void Clear();
|
||||
|
||||
void DeleteByAddress(u32 _Address);
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ public:
|
|||
virtual void Video_GatherPipeBursted() = 0;
|
||||
|
||||
virtual bool Video_IsPossibleWaitingSetDrawDone() = 0;
|
||||
virtual bool Video_IsHiWatermarkActive() = 0;
|
||||
virtual void Video_AbortFrame() = 0;
|
||||
|
||||
virtual readFn16 Video_CPRead16() = 0;
|
||||
|
@ -159,6 +160,7 @@ class VideoBackendHardware : public VideoBackend
|
|||
void Video_GatherPipeBursted();
|
||||
|
||||
bool Video_IsPossibleWaitingSetDrawDone();
|
||||
bool Video_IsHiWatermarkActive();
|
||||
void Video_AbortFrame();
|
||||
|
||||
readFn16 Video_CPRead16();
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
#include "ChunkFile.h"
|
||||
#include "ProcessorInterface.h"
|
||||
#include "Memmap.h"
|
||||
#include "../PowerPC/PowerPC.h"
|
||||
|
||||
#include "VideoBackendBase.h"
|
||||
#include "../PowerPC/JitCommon/JitBase.h"
|
||||
#include "../PowerPC/PowerPC.h"
|
||||
|
||||
#include "GPFifo.h"
|
||||
|
||||
|
@ -96,6 +96,15 @@ void STACKALIGN CheckGatherPipe()
|
|||
|
||||
// move back the spill bytes
|
||||
memmove(m_gatherPipe, m_gatherPipe + cnt, m_gatherPipeCount);
|
||||
|
||||
// Profile where the FIFO writes are occurring.
|
||||
if (jit && (jit->js.fifoWriteAddresses.find(PC)) == (jit->js.fifoWriteAddresses.end()))
|
||||
{
|
||||
jit->js.fifoWriteAddresses.insert(PC);
|
||||
|
||||
// Invalidate the JIT block so that it gets recompiled with the external exception check included.
|
||||
jit->GetBlockCache()->InvalidateICache(PC, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -367,7 +367,7 @@ void Interpreter::dcbf(UGeckoInstruction _inst)
|
|||
if (jit)
|
||||
{
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
jit->GetBlockCache()->InvalidateICache(address & ~0x1f);
|
||||
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,7 +378,7 @@ void Interpreter::dcbi(UGeckoInstruction _inst)
|
|||
if (jit)
|
||||
{
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
jit->GetBlockCache()->InvalidateICache(address & ~0x1f);
|
||||
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -414,8 +414,8 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
|||
// Comment out the following to disable breakpoints (speed-up)
|
||||
if (!Profiler::g_ProfileBlocks)
|
||||
{
|
||||
blockSize = 1;
|
||||
broken_block = true;
|
||||
if (GetState() == CPU_STEPPING)
|
||||
blockSize = 1;
|
||||
Trace();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,12 +81,15 @@ void Jit64AsmRoutineManager::Generate()
|
|||
|
||||
if (Core::g_CoreStartupParameter.bEnableDebugging)
|
||||
{
|
||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(PowerPC::CPU_STEPPING));
|
||||
FixupBranch notStepping = J_CC(CC_Z);
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckBreakPoints));
|
||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
||||
FixupBranch noBreakpoint = J_CC(CC_Z);
|
||||
ABI_PopAllCalleeSavedRegsAndAdjustStack();
|
||||
RET();
|
||||
SetJumpTarget(noBreakpoint);
|
||||
SetJumpTarget(notStepping);
|
||||
}
|
||||
|
||||
SetJumpTarget(skipToRealDispatch);
|
||||
|
|
|
@ -266,7 +266,7 @@ void Jit64::reg_imm(UGeckoInstruction inst)
|
|||
case 15:
|
||||
if (a == 0) { // lis
|
||||
// Merge with next instruction if loading a 32-bits immediate value (lis + addi, lis + ori)
|
||||
if (!js.isLastInstruction) {
|
||||
if (!js.isLastInstruction && !Core::g_CoreStartupParameter.bEnableDebugging) {
|
||||
if ((js.next_inst.OPCD == 14) && (js.next_inst.RD == d) && (js.next_inst.RA == d)) { // addi
|
||||
gpr.SetImmediate32(d, ((u32)inst.SIMM_16 << 16) + (u32)(s32)js.next_inst.SIMM_16);
|
||||
js.downcountAmount++;
|
||||
|
|
|
@ -1260,8 +1260,8 @@ static const std::string opcodeNames[] = {
|
|||
"FResult_End", "StorePaired", "StoreSingle", "StoreDouble", "StoreFReg",
|
||||
"FDCmpCR", "CInt16", "CInt32", "SystemCall", "RFIExit",
|
||||
"InterpreterBranch", "IdleBranch", "ShortIdleLoop",
|
||||
"FPExceptionCheckStart", "FPExceptionCheckEnd", "ISIException", "Tramp",
|
||||
"BlockStart", "BlockEnd", "Int3",
|
||||
"FPExceptionCheckStart", "FPExceptionCheckEnd", "ISIException", "ExtExceptionCheck",
|
||||
"Tramp", "BlockStart", "BlockEnd", "Int3",
|
||||
};
|
||||
static const unsigned alwaysUsedList[] = {
|
||||
InterpreterFallback, StoreGReg, StoreCR, StoreLink, StoreCTR, StoreMSR,
|
||||
|
@ -1269,7 +1269,8 @@ static const unsigned alwaysUsedList[] = {
|
|||
Store16, Store32, StoreSingle, StoreDouble, StorePaired, StoreFReg, FDCmpCR,
|
||||
BlockStart, BlockEnd, IdleBranch, BranchCond, BranchUncond, ShortIdleLoop,
|
||||
SystemCall, InterpreterBranch, RFIExit, FPExceptionCheckStart,
|
||||
FPExceptionCheckEnd, ISIException, Int3, Tramp, Nop
|
||||
FPExceptionCheckEnd, ISIException, ExtExceptionCheck, BreakPointCheck,
|
||||
Int3, Tramp, Nop
|
||||
};
|
||||
static const unsigned extra8RegList[] = {
|
||||
LoadGReg, LoadCR, LoadGQR, LoadFReg, LoadFRegDENToZero,
|
||||
|
|
|
@ -165,10 +165,10 @@ enum Opcode {
|
|||
ShortIdleLoop, // Idle loop seen in homebrew like wii mahjong,
|
||||
// just a branch
|
||||
|
||||
// used for MMU, at least until someone
|
||||
// used for exception checking, at least until someone
|
||||
// has a better idea of integrating it
|
||||
FPExceptionCheckStart, FPExceptionCheckEnd,
|
||||
ISIException,
|
||||
ISIException, ExtExceptionCheck, BreakPointCheck,
|
||||
// "Opcode" representing a register too far away to
|
||||
// reference directly; this is a size optimization
|
||||
Tramp,
|
||||
|
@ -411,6 +411,12 @@ public:
|
|||
InstLoc EmitISIException(InstLoc dest) {
|
||||
return EmitUOp(ISIException, dest);
|
||||
}
|
||||
InstLoc EmitExtExceptionCheck(InstLoc pc) {
|
||||
return EmitUOp(ExtExceptionCheck, pc);
|
||||
}
|
||||
InstLoc EmitBreakPointCheck(InstLoc pc) {
|
||||
return EmitUOp(BreakPointCheck, pc);
|
||||
}
|
||||
InstLoc EmitRFIExit() {
|
||||
return FoldZeroOp(RFIExit, 0);
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ The register allocation is linear scan allocation.
|
|||
#include "../../../../Common/Src/CPUDetect.h"
|
||||
#include "MathUtil.h"
|
||||
#include "../../Core.h"
|
||||
#include "HW/ProcessorInterface.h"
|
||||
|
||||
static ThunkManager thunks;
|
||||
|
||||
|
@ -761,6 +762,8 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak
|
|||
case FPExceptionCheckStart:
|
||||
case FPExceptionCheckEnd:
|
||||
case ISIException:
|
||||
case ExtExceptionCheck:
|
||||
case BreakPointCheck:
|
||||
case Int3:
|
||||
case Tramp:
|
||||
// No liveness effects
|
||||
|
@ -1920,6 +1923,38 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak
|
|||
Jit->WriteExceptionExit();
|
||||
break;
|
||||
}
|
||||
case ExtExceptionCheck: {
|
||||
unsigned InstLoc = ibuild->GetImmValue(getOp1(I));
|
||||
|
||||
Jit->TEST(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_ISI | EXCEPTION_PROGRAM | EXCEPTION_SYSCALL | EXCEPTION_FPU_UNAVAILABLE | EXCEPTION_DSI | EXCEPTION_ALIGNMENT | EXCEPTION_DECREMENTER));
|
||||
FixupBranch clearInt = Jit->J_CC(CC_NZ);
|
||||
Jit->TEST(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_EXTERNAL_INT));
|
||||
FixupBranch noExtException = Jit->J_CC(CC_Z);
|
||||
Jit->TEST(32, M((void *)&PowerPC::ppcState.msr), Imm32(0x0008000));
|
||||
FixupBranch noExtIntEnable = Jit->J_CC(CC_Z);
|
||||
Jit->TEST(32, M((void *)&ProcessorInterface::m_InterruptCause), Imm32(ProcessorInterface::INT_CAUSE_CP || ProcessorInterface::INT_CAUSE_PE_TOKEN || ProcessorInterface::INT_CAUSE_PE_FINISH));
|
||||
FixupBranch noCPInt = Jit->J_CC(CC_Z);
|
||||
|
||||
Jit->MOV(32, M(&PC), Imm32(InstLoc));
|
||||
Jit->WriteExceptionExit();
|
||||
|
||||
Jit->SetJumpTarget(noCPInt);
|
||||
Jit->SetJumpTarget(noExtIntEnable);
|
||||
Jit->SetJumpTarget(noExtException);
|
||||
Jit->SetJumpTarget(clearInt);
|
||||
break;
|
||||
}
|
||||
case BreakPointCheck: {
|
||||
unsigned InstLoc = ibuild->GetImmValue(getOp1(I));
|
||||
|
||||
Jit->MOV(32, M(&PC), Imm32(InstLoc));
|
||||
Jit->ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckBreakPoints));
|
||||
Jit->TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
||||
FixupBranch noBreakpoint = Jit->J_CC(CC_Z);
|
||||
Jit->WriteExit(InstLoc, 0);
|
||||
Jit->SetJumpTarget(noBreakpoint);
|
||||
break;
|
||||
}
|
||||
case Int3: {
|
||||
Jit->INT3();
|
||||
break;
|
||||
|
|
|
@ -526,7 +526,8 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
|||
// Comment out the following to disable breakpoints (speed-up)
|
||||
if (!Profiler::g_ProfileBlocks)
|
||||
{
|
||||
blockSize = 1;
|
||||
if (GetState() == CPU_STEPPING)
|
||||
blockSize = 1;
|
||||
Trace();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,12 +83,15 @@ void JitILAsmRoutineManager::Generate()
|
|||
|
||||
if (Core::g_CoreStartupParameter.bEnableDebugging)
|
||||
{
|
||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(PowerPC::CPU_STEPPING));
|
||||
FixupBranch notStepping = J_CC(CC_Z);
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckBreakPoints));
|
||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
||||
FixupBranch noBreakpoint = J_CC(CC_Z);
|
||||
ABI_PopAllCalleeSavedRegsAndAdjustStack();
|
||||
RET();
|
||||
SetJumpTarget(noBreakpoint);
|
||||
SetJumpTarget(notStepping);
|
||||
}
|
||||
|
||||
SetJumpTarget(skipToRealDispatch);
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
#include "JitBase.h"
|
||||
#include "PowerPCDisasm.h"
|
||||
#include "disasm.h"
|
||||
|
||||
JitBase *jit;
|
||||
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
#include "JitBackpatch.h" // for EmuCodeBlock
|
||||
#include "JitAsmCommon.h"
|
||||
|
||||
#include "PowerPCDisasm.h"
|
||||
#include "disasm.h"
|
||||
#include <set>
|
||||
|
||||
#define JIT_OPCODE 0
|
||||
|
||||
|
@ -75,6 +74,8 @@ protected:
|
|||
u8* rewriteStart;
|
||||
|
||||
JitBlock *curBlock;
|
||||
|
||||
std::set<u32> fifoWriteAddresses;
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
|
@ -353,6 +353,24 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
}
|
||||
}
|
||||
|
||||
void JitBlockCache::UnlinkBlock(int i)
|
||||
{
|
||||
JitBlock &b = blocks[i];
|
||||
std::map<u32, int>::iterator iter;
|
||||
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
|
||||
ppp = links_to.equal_range(b.originalAddress);
|
||||
if (ppp.first == ppp.second)
|
||||
return;
|
||||
for (multimap<u32, int>::iterator iter2 = ppp.first; iter2 != ppp.second; ++iter2) {
|
||||
JitBlock &sourceBlock = blocks[iter2->second];
|
||||
for (int e = 0; e < 2; e++)
|
||||
{
|
||||
if (sourceBlock.exitAddress[e] == b.originalAddress)
|
||||
sourceBlock.linkStatus[e] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JitBlockCache::DestroyBlock(int block_num, bool invalidate)
|
||||
{
|
||||
if (block_num < 0 || block_num >= num_blocks)
|
||||
|
@ -375,7 +393,9 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress);
|
||||
#endif
|
||||
|
||||
// We don't unlink blocks, we just send anyone who tries to run them back to the dispatcher.
|
||||
UnlinkBlock(block_num);
|
||||
|
||||
// Send anyone who tries to run this block back to the dispatcher.
|
||||
// Not entirely ideal, but .. pretty good.
|
||||
// Spurious entrances from previously linked blocks can only come through checkedEntry
|
||||
XEmitter emit((u8 *)b.checkedEntry);
|
||||
|
@ -390,14 +410,31 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
}
|
||||
|
||||
|
||||
void JitBlockCache::InvalidateICache(u32 address)
|
||||
void JitBlockCache::InvalidateICache(u32 address, const u32 length)
|
||||
{
|
||||
address &= ~0x1f;
|
||||
// destroy JIT blocks
|
||||
// !! this works correctly under assumption that any two overlapping blocks end at the same address
|
||||
std::map<pair<u32,u32>, u32>::iterator it1 = block_map.lower_bound(std::make_pair(address, 0)), it2 = it1, it;
|
||||
while (it2 != block_map.end() && it2->first.second < address + 0x20)
|
||||
while (it2 != block_map.end() && it2->first.second < address + length)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
JitBlock &b = blocks[it2->second];
|
||||
if (b.originalAddress & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
u32 cacheaddr = b.originalAddress & JIT_ICACHE_MASK;
|
||||
memset(iCacheVMEM + cacheaddr, JIT_ICACHE_INVALID_BYTE, 4);
|
||||
}
|
||||
else if (b.originalAddress & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
u32 cacheaddr = b.originalAddress & JIT_ICACHEEX_MASK;
|
||||
memset(iCacheEx + cacheaddr, JIT_ICACHE_INVALID_BYTE, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 cacheaddr = b.originalAddress & JIT_ICACHE_MASK;
|
||||
memset(iCache + cacheaddr, JIT_ICACHE_INVALID_BYTE, 4);
|
||||
}
|
||||
#endif
|
||||
DestroyBlock(it2->second, true);
|
||||
it2++;
|
||||
}
|
||||
|
@ -418,17 +455,17 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
if (address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
u32 cacheaddr = address & JIT_ICACHE_MASK;
|
||||
memset(iCacheVMEM + cacheaddr, JIT_ICACHE_INVALID_BYTE, 32);
|
||||
memset(iCacheVMEM + cacheaddr, JIT_ICACHE_INVALID_BYTE, length);
|
||||
}
|
||||
else if (address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
u32 cacheaddr = address & JIT_ICACHEEX_MASK;
|
||||
memset(iCacheEx + cacheaddr, JIT_ICACHE_INVALID_BYTE, 32);
|
||||
memset(iCacheEx + cacheaddr, JIT_ICACHE_INVALID_BYTE, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 cacheaddr = address & JIT_ICACHE_MASK;
|
||||
memset(iCache + cacheaddr, JIT_ICACHE_INVALID_BYTE, 32);
|
||||
memset(iCache + cacheaddr, JIT_ICACHE_INVALID_BYTE, length);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ class JitBlockCache
|
|||
bool RangeIntersect(int s1, int e1, int s2, int e2) const;
|
||||
void LinkBlockExits(int i);
|
||||
void LinkBlock(int i);
|
||||
void UnlinkBlock(int i);
|
||||
|
||||
public:
|
||||
JitBlockCache() :
|
||||
|
@ -129,7 +130,7 @@ public:
|
|||
CompiledCode GetCompiledCodeFromBlock(int block_num);
|
||||
|
||||
// DOES NOT WORK CORRECTLY WITH INLINING
|
||||
void InvalidateICache(u32 em_address);
|
||||
void InvalidateICache(u32 address, const u32 length);
|
||||
void DestroyBlock(int block_num, bool invalidate);
|
||||
|
||||
// Not currently used
|
||||
|
|
|
@ -110,7 +110,7 @@ namespace PowerPC
|
|||
#endif
|
||||
valid[set] = 0;
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(addr);
|
||||
jit->GetBlockCache()->InvalidateICache(addr & ~0x1f, 32);
|
||||
}
|
||||
|
||||
u32 InstructionCache::ReadInstruction(u32 addr)
|
||||
|
|
|
@ -263,6 +263,8 @@ void CCodeWindow::SingleStep()
|
|||
{
|
||||
if (CCPU::IsStepping())
|
||||
{
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(PC, 4);
|
||||
CCPU::StepOpcode(&sync_event);
|
||||
wxThread::Sleep(20);
|
||||
// need a short wait here
|
||||
|
|
|
@ -56,11 +56,12 @@ static bool bProcessFifoToLoWatermark = false;
|
|||
static bool bProcessFifoAllDistance = false;
|
||||
|
||||
volatile bool isPossibleWaitingSetDrawDone = false;
|
||||
volatile bool isHiWatermarkActive = false;
|
||||
volatile bool interruptSet= false;
|
||||
volatile bool interruptWaiting= false;
|
||||
volatile bool interruptTokenWaiting = false;
|
||||
volatile bool interruptFinishWaiting = false;
|
||||
volatile bool OnOverflow = false;
|
||||
volatile bool waitingForPEInterruptDisable = false;
|
||||
|
||||
bool IsOnThread()
|
||||
{
|
||||
|
@ -86,13 +87,12 @@ void DoState(PointerWrap &p)
|
|||
|
||||
p.Do(bProcessFifoToLoWatermark);
|
||||
p.Do(bProcessFifoAllDistance);
|
||||
|
||||
p.Do(isHiWatermarkActive);
|
||||
p.Do(isPossibleWaitingSetDrawDone);
|
||||
p.Do(interruptSet);
|
||||
p.Do(interruptWaiting);
|
||||
p.Do(interruptTokenWaiting);
|
||||
p.Do(interruptFinishWaiting);
|
||||
p.Do(OnOverflow);
|
||||
}
|
||||
|
||||
inline void WriteLow (volatile u32& _reg, u16 lowbits) {Common::AtomicStore(_reg,(_reg & 0xFFFF0000) | lowbits);}
|
||||
|
@ -135,16 +135,14 @@ void Init()
|
|||
bProcessFifoToLoWatermark = false;
|
||||
bProcessFifoAllDistance = false;
|
||||
isPossibleWaitingSetDrawDone = false;
|
||||
OnOverflow = false;
|
||||
isHiWatermarkActive = false;
|
||||
|
||||
et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper);
|
||||
}
|
||||
|
||||
void Read16(u16& _rReturnValue, const u32 _Address)
|
||||
{
|
||||
|
||||
INFO_LOG(COMMANDPROCESSOR, "(r): 0x%08x", _Address);
|
||||
ProcessFifoEvents();
|
||||
switch (_Address & 0xFFF)
|
||||
{
|
||||
case STATUS_REGISTER:
|
||||
|
@ -173,11 +171,23 @@ void Read16(u16& _rReturnValue, const u32 _Address)
|
|||
case FIFO_LO_WATERMARK_HI: _rReturnValue = ReadHigh(fifo.CPLoWatermark); return;
|
||||
|
||||
case FIFO_RW_DISTANCE_LO:
|
||||
_rReturnValue = ReadLow (fifo.CPReadWriteDistance);
|
||||
if (IsOnThread())
|
||||
if(fifo.CPWritePointer >= fifo.SafeCPReadPointer)
|
||||
_rReturnValue = ReadLow (fifo.CPWritePointer - fifo.SafeCPReadPointer);
|
||||
else
|
||||
_rReturnValue = ReadLow (fifo.CPEnd - fifo.SafeCPReadPointer + fifo.CPWritePointer - fifo.CPBase + 32);
|
||||
else
|
||||
_rReturnValue = ReadLow (fifo.CPReadWriteDistance);
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_RW_DISTANCE_LO : %04x", _rReturnValue);
|
||||
return;
|
||||
case FIFO_RW_DISTANCE_HI:
|
||||
_rReturnValue = ReadHigh(fifo.CPReadWriteDistance);
|
||||
if (IsOnThread())
|
||||
if(fifo.CPWritePointer >= fifo.SafeCPReadPointer)
|
||||
_rReturnValue = ReadHigh (fifo.CPWritePointer - fifo.SafeCPReadPointer);
|
||||
else
|
||||
_rReturnValue = ReadHigh (fifo.CPEnd - fifo.SafeCPReadPointer + fifo.CPWritePointer - fifo.CPBase + 32);
|
||||
else
|
||||
_rReturnValue = ReadHigh(fifo.CPReadWriteDistance);
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_RW_DISTANCE_HI : %04x", _rReturnValue);
|
||||
return;
|
||||
case FIFO_WRITE_POINTER_LO:
|
||||
|
@ -358,6 +368,7 @@ void Write16(const u16 _Value, const u32 _Address)
|
|||
break;
|
||||
case FIFO_READ_POINTER_HI:
|
||||
WriteHigh((u32 &)fifo.CPReadPointer, _Value);
|
||||
fifo.SafeCPReadPointer = fifo.CPReadPointer;
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_READ_POINTER_HI : %04x", _Value);
|
||||
break;
|
||||
|
||||
|
@ -390,10 +401,6 @@ void Write16(const u16 _Value, const u32 _Address)
|
|||
|
||||
case FIFO_RW_DISTANCE_HI:
|
||||
WriteHigh((u32 &)fifo.CPReadWriteDistance, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_HI : %04x", _Value);
|
||||
break;
|
||||
case FIFO_RW_DISTANCE_LO:
|
||||
WriteLow((u32 &)fifo.CPReadWriteDistance, _Value & 0xFFE0);
|
||||
if (fifo.CPReadWriteDistance == 0)
|
||||
{
|
||||
GPFifo::ResetGatherPipe();
|
||||
|
@ -403,6 +410,10 @@ void Write16(const u16 _Value, const u32 _Address)
|
|||
ResetVideoBuffer();
|
||||
}
|
||||
IncrementCheckContextId();
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_HI : %04x", _Value);
|
||||
break;
|
||||
case FIFO_RW_DISTANCE_LO:
|
||||
WriteLow((u32 &)fifo.CPReadWriteDistance, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_LO : %04x", _Value);
|
||||
break;
|
||||
|
||||
|
@ -412,7 +423,6 @@ void Write16(const u16 _Value, const u32 _Address)
|
|||
|
||||
if (!IsOnThread())
|
||||
RunGpu();
|
||||
ProcessFifoEvents();
|
||||
}
|
||||
|
||||
void Read32(u32& _rReturnValue, const u32 _Address)
|
||||
|
@ -434,6 +444,19 @@ void STACKALIGN GatherPipeBursted()
|
|||
{
|
||||
if (!IsOnThread())
|
||||
RunGpu();
|
||||
else
|
||||
{
|
||||
// In multibuffer mode is not allowed write in the same fifo attached to the GPU.
|
||||
// Fix Pokemon XD in DC mode.
|
||||
if((ProcessorInterface::Fifo_CPUEnd == fifo.CPEnd) && (ProcessorInterface::Fifo_CPUBase == fifo.CPBase)
|
||||
&& fifo.CPReadWriteDistance > 0)
|
||||
{
|
||||
waitingForPEInterruptDisable = true;
|
||||
ProcessFifoAllDistance();
|
||||
waitingForPEInterruptDisable = false;
|
||||
}
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -449,26 +472,7 @@ void STACKALIGN GatherPipeBursted()
|
|||
Common::AtomicAdd(fifo.CPReadWriteDistance, GATHER_PIPE_SIZE);
|
||||
|
||||
if (!IsOnThread())
|
||||
{
|
||||
RunGpu();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(fifo.CPReadWriteDistance == fifo.CPEnd - fifo.CPBase - 32)
|
||||
{
|
||||
if(!OnOverflow)
|
||||
NOTICE_LOG(COMMANDPROCESSOR,"FIFO is almost in overflown, BreakPoint: %i", fifo.bFF_Breakpoint);
|
||||
OnOverflow = true;
|
||||
while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable &&
|
||||
fifo.CPReadWriteDistance > fifo.CPEnd - fifo.CPBase - 64)
|
||||
Common::YieldCPU();
|
||||
}
|
||||
else
|
||||
{
|
||||
OnOverflow = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance <= fifo.CPEnd - fifo.CPBase,
|
||||
"FIFO is overflown by GatherPipe !\nCPU thread is too fast!");
|
||||
|
@ -509,17 +513,15 @@ void AbortFrame()
|
|||
|
||||
void SetOverflowStatusFromGatherPipe()
|
||||
{
|
||||
if (!fifo.bFF_HiWatermarkInt) return;
|
||||
|
||||
fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark);
|
||||
fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark);
|
||||
|
||||
bool interrupt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt &&
|
||||
m_CPCtrlReg.GPLinkEnable && m_CPCtrlReg.GPReadEnable;
|
||||
isHiWatermarkActive = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt && m_CPCtrlReg.GPReadEnable;
|
||||
|
||||
if (interrupt != interruptSet && interrupt)
|
||||
CommandProcessor::UpdateInterrupts(true);
|
||||
|
||||
if (isHiWatermarkActive)
|
||||
{
|
||||
interruptSet = true;
|
||||
INFO_LOG(COMMANDPROCESSOR,"Interrupt set");
|
||||
ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true);
|
||||
}
|
||||
}
|
||||
|
||||
void SetCpStatus()
|
||||
|
@ -527,14 +529,12 @@ void SetCpStatus()
|
|||
// overflow & underflow check
|
||||
fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark);
|
||||
fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark);
|
||||
|
||||
// breakpoint
|
||||
|
||||
// breakpoint
|
||||
if (fifo.bFF_BPEnable)
|
||||
{
|
||||
if (fifo.CPBreakpoint == fifo.CPReadPointer)
|
||||
{
|
||||
|
||||
{
|
||||
if (!fifo.bFF_Breakpoint)
|
||||
{
|
||||
INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer);
|
||||
|
@ -562,13 +562,18 @@ void SetCpStatus()
|
|||
|
||||
bool interrupt = (bpInt || ovfInt || undfInt) && m_CPCtrlReg.GPReadEnable;
|
||||
|
||||
isHiWatermarkActive = ovfInt && m_CPCtrlReg.GPReadEnable;
|
||||
|
||||
if (interrupt != interruptSet && !interruptWaiting)
|
||||
{
|
||||
u64 userdata = interrupt?1:0;
|
||||
if (IsOnThread())
|
||||
{
|
||||
interruptWaiting = true;
|
||||
CommandProcessor::UpdateInterruptsFromVideoBackend(userdata);
|
||||
if(!interrupt || bpInt || undfInt)
|
||||
{
|
||||
interruptWaiting = true;
|
||||
CommandProcessor::UpdateInterruptsFromVideoBackend(userdata);
|
||||
}
|
||||
}
|
||||
else
|
||||
CommandProcessor::UpdateInterrupts(userdata);
|
||||
|
@ -591,7 +596,7 @@ void ProcessFifoAllDistance()
|
|||
if (IsOnThread())
|
||||
{
|
||||
while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable &&
|
||||
fifo.CPReadWriteDistance && !AtBreakpoint())
|
||||
fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt())
|
||||
Common::YieldCPU();
|
||||
}
|
||||
bProcessFifoAllDistance = false;
|
||||
|
@ -611,13 +616,16 @@ void Shutdown()
|
|||
void SetCpStatusRegister()
|
||||
{
|
||||
// Here always there is one fifo attached to the GPU
|
||||
|
||||
m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint;
|
||||
m_CPStatusReg.ReadIdle = (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint);
|
||||
m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ;
|
||||
m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance;
|
||||
m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark;
|
||||
m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark;
|
||||
|
||||
// HACK to compensate for slow response to PE interrupts in Time Splitters: Future Perfect
|
||||
if (IsOnThread())
|
||||
PixelEngine::ResumeWaitingForPEInterrupt();
|
||||
|
||||
INFO_LOG(COMMANDPROCESSOR,"\t Read from STATUS_REGISTER : %04x", m_CPStatusReg.Hex);
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "(r) status: iBP %s | fReadIdle %s | fCmdIdle %s | iOvF %s | iUndF %s"
|
||||
, m_CPStatusReg.Breakpoint ? "ON" : "OFF"
|
||||
|
@ -630,14 +638,14 @@ void SetCpStatusRegister()
|
|||
|
||||
void SetCpControlRegister()
|
||||
{
|
||||
|
||||
// If the new fifo is being attached We make sure there wont be SetFinish event pending.
|
||||
// This protection fix eternal darkness booting, because the second SetFinish event when it is booting
|
||||
// seems invalid or has a bug and hang the game.
|
||||
|
||||
if (!fifo.bFF_GPReadEnable && m_CPCtrlReg.GPReadEnable && !m_CPCtrlReg.BPEnable)
|
||||
{
|
||||
PixelEngine::ResetSetFinish();
|
||||
ProcessFifoEvents();
|
||||
PixelEngine::ResetSetFinish();
|
||||
}
|
||||
|
||||
fifo.bFF_BPInt = m_CPCtrlReg.BPInt;
|
||||
|
@ -652,9 +660,6 @@ void SetCpControlRegister()
|
|||
ProcessorInterface::Fifo_CPUBase = fifo.CPBase;
|
||||
ProcessorInterface::Fifo_CPUEnd = fifo.CPEnd;
|
||||
}
|
||||
// If overflown happens process the fifo to LoWatemark
|
||||
if (bProcessFifoToLoWatermark)
|
||||
ProcessFifoToLoWatermark();
|
||||
|
||||
if(fifo.bFF_GPReadEnable && !m_CPCtrlReg.GPReadEnable)
|
||||
{
|
||||
|
@ -666,7 +671,6 @@ void SetCpControlRegister()
|
|||
fifo.bFF_GPReadEnable = m_CPCtrlReg.GPReadEnable;
|
||||
}
|
||||
|
||||
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "\t GPREAD %s | BP %s | Int %s | OvF %s | UndF %s | LINK %s"
|
||||
, fifo.bFF_GPReadEnable ? "ON" : "OFF"
|
||||
, fifo.bFF_BPEnable ? "ON" : "OFF"
|
||||
|
|
|
@ -25,18 +25,18 @@ class PointerWrap;
|
|||
|
||||
extern bool MT;
|
||||
|
||||
|
||||
namespace CommandProcessor
|
||||
{
|
||||
|
||||
extern SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread.
|
||||
extern volatile bool isPossibleWaitingSetDrawDone; //This one is used for sync gfx thread and emulator thread.
|
||||
extern volatile bool isHiWatermarkActive;
|
||||
extern volatile bool interruptSet;
|
||||
extern volatile bool interruptWaiting;
|
||||
extern volatile bool interruptTokenWaiting;
|
||||
extern volatile bool interruptFinishWaiting;
|
||||
extern volatile bool OnOverflow;
|
||||
|
||||
extern volatile bool waitingForPEInterruptDisable;
|
||||
|
||||
// internal hardware addresses
|
||||
enum
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "Atomic.h"
|
||||
#include "OpcodeDecoding.h"
|
||||
#include "CommandProcessor.h"
|
||||
#include "PixelEngine.h"
|
||||
#include "ChunkFile.h"
|
||||
#include "Fifo.h"
|
||||
#include "HW/Memmap.h"
|
||||
|
@ -137,8 +138,7 @@ void RunGpuLoop()
|
|||
|
||||
CommandProcessor::SetCpStatus();
|
||||
// check if we are able to run this buffer
|
||||
while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable &&
|
||||
fifo.CPReadWriteDistance && (!AtBreakpoint() || CommandProcessor::OnOverflow))
|
||||
while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt())
|
||||
{
|
||||
if (!GpuRunningState) break;
|
||||
|
||||
|
|
|
@ -250,6 +250,11 @@ bool VideoBackendHardware::Video_IsPossibleWaitingSetDrawDone()
|
|||
return CommandProcessor::isPossibleWaitingSetDrawDone;
|
||||
}
|
||||
|
||||
bool VideoBackendHardware::Video_IsHiWatermarkActive()
|
||||
{
|
||||
return CommandProcessor::isHiWatermarkActive;
|
||||
}
|
||||
|
||||
void VideoBackendHardware::Video_AbortFrame()
|
||||
{
|
||||
CommandProcessor::AbortFrame();
|
||||
|
|
|
@ -180,7 +180,6 @@ void Init()
|
|||
void Read16(u16& _uReturnValue, const u32 _iAddress)
|
||||
{
|
||||
DEBUG_LOG(PIXELENGINE, "(r16) 0x%08x", _iAddress);
|
||||
CommandProcessor::ProcessFifoEvents();
|
||||
switch (_iAddress & 0xFFF)
|
||||
{
|
||||
// CPU Direct Access EFB Raster State Config
|
||||
|
@ -327,7 +326,6 @@ void Write16(const u16 _iValue, const u32 _iAddress)
|
|||
break;
|
||||
|
||||
case PE_TOKEN_REG:
|
||||
//LOG(PIXELENGINE,"WEIRD: program wrote token: %i",_iValue);
|
||||
PanicAlert("(w16) WTF? PowerPC program wrote token: %i", _iValue);
|
||||
//only the gx pipeline is supposed to be able to write here
|
||||
//g_token = _iValue;
|
||||
|
@ -338,7 +336,6 @@ void Write16(const u16 _iValue, const u32 _iAddress)
|
|||
break;
|
||||
}
|
||||
|
||||
CommandProcessor::ProcessFifoEvents();
|
||||
}
|
||||
|
||||
void Write32(const u32 _iValue, const u32 _iAddress)
|
||||
|
@ -362,22 +359,16 @@ void UpdateInterrupts()
|
|||
|
||||
void UpdateTokenInterrupt(bool active)
|
||||
{
|
||||
if(interruptSetToken != active)
|
||||
{
|
||||
ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active);
|
||||
interruptSetToken = active;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateFinishInterrupt(bool active)
|
||||
{
|
||||
if(interruptSetFinish != active)
|
||||
{
|
||||
ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active);
|
||||
interruptSetFinish = active;
|
||||
if (active)
|
||||
State::ProcessRequestedStates(0);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(mb2): Refactor SetTokenINT_OnMainThread(u64 userdata, int cyclesLate).
|
||||
|
@ -396,8 +387,6 @@ void SetToken_OnMainThread(u64 userdata, int cyclesLate)
|
|||
CommandProcessor::interruptTokenWaiting = false;
|
||||
IncrementCheckContextId();
|
||||
//}
|
||||
//else
|
||||
// LOGV(PIXELENGINE, 1, "VIDEO Backend wrote token: %i", CommandProcessor::fifo.PEToken);
|
||||
}
|
||||
|
||||
void SetFinish_OnMainThread(u64 userdata, int cyclesLate)
|
||||
|
@ -474,4 +463,17 @@ void ResetSetToken()
|
|||
}
|
||||
CommandProcessor::interruptTokenWaiting = false;
|
||||
}
|
||||
|
||||
bool WaitingForPEInterrupt()
|
||||
{
|
||||
return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || interruptSetFinish || interruptSetToken);
|
||||
}
|
||||
|
||||
void ResumeWaitingForPEInterrupt()
|
||||
{
|
||||
interruptSetFinish = false;
|
||||
interruptSetToken = false;
|
||||
CommandProcessor::interruptFinishWaiting = false;
|
||||
CommandProcessor::interruptTokenWaiting = false;
|
||||
}
|
||||
} // end of namespace PixelEngine
|
||||
|
|
|
@ -80,7 +80,8 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge);
|
|||
void SetFinish(void);
|
||||
void ResetSetFinish(void);
|
||||
void ResetSetToken(void);
|
||||
bool AllowIdleSkipping();
|
||||
bool WaitingForPEInterrupt();
|
||||
void ResumeWaitingForPEInterrupt();
|
||||
|
||||
// Bounding box functionality. Paper Mario (both) are a couple of the few games that use it.
|
||||
extern u16 bbox[4];
|
||||
|
|
|
@ -215,6 +215,12 @@ bool VideoSoftware::Video_IsPossibleWaitingSetDrawDone(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool VideoSoftware::Video_IsHiWatermarkActive(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void VideoSoftware::Video_AbortFrame(void)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ class VideoSoftware : public VideoBackend
|
|||
void Video_SetRendering(bool bEnabled);
|
||||
|
||||
void Video_GatherPipeBursted();
|
||||
|
||||
bool Video_IsHiWatermarkActive();
|
||||
bool Video_IsPossibleWaitingSetDrawDone();
|
||||
void Video_AbortFrame();
|
||||
|
||||
|
|
Loading…
Reference in New Issue