Switch to Video_BeginField; hopefully fix or reduce some video stability problems by using Events.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@3740 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Nolan Check 2009-07-11 02:34:16 +00:00
parent 2a236a4631
commit dc7d9ab998
13 changed files with 185 additions and 166 deletions

View File

@ -24,7 +24,7 @@ PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo
{
Video_Prepare = 0;
Video_SendFifoData = 0;
Video_UpdateXFB = 0;
Video_BeginField = 0;
Video_EnterLoop = 0;
Video_ExitLoop = 0;
Video_Screenshot = 0;
@ -35,8 +35,8 @@ PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo
(LoadSymbol("Video_Prepare"));
Video_SendFifoData = reinterpret_cast<TVideo_SendFifoData>
(LoadSymbol("Video_SendFifoData"));
Video_UpdateXFB = reinterpret_cast<TVideo_UpdateXFB>
(LoadSymbol("Video_UpdateXFB"));
Video_BeginField = reinterpret_cast<TVideo_BeginField>
(LoadSymbol("Video_BeginField"));
Video_Screenshot = reinterpret_cast<TVideo_Screenshot>
(LoadSymbol("Video_Screenshot"));
Video_EnterLoop = reinterpret_cast<TVideo_EnterLoop>
@ -50,7 +50,7 @@ PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo
if ((Video_Prepare != 0) &&
(Video_SendFifoData != 0) &&
(Video_UpdateXFB != 0) &&
(Video_BeginField != 0) &&
(Video_EnterLoop != 0) &&
(Video_ExitLoop != 0) &&
(Video_Screenshot != 0) &&

View File

@ -25,7 +25,7 @@ namespace Common {
typedef void (__cdecl* TVideo_Prepare)();
typedef void (__cdecl* TVideo_SendFifoData)(u8*,u32);
typedef void (__cdecl* TVideo_UpdateXFB)(u32, u32, u32, s32, bool);
typedef void (__cdecl* TVideo_BeginField)(u32, FieldType, u32, u32);
typedef bool (__cdecl* TVideo_Screenshot)(const char* filename);
typedef void (__cdecl* TVideo_EnterLoop)();
typedef void (__cdecl* TVideo_ExitLoop)();
@ -43,7 +43,7 @@ public:
TVideo_SendFifoData Video_SendFifoData;
TVideo_EnterLoop Video_EnterLoop;
TVideo_ExitLoop Video_ExitLoop;
TVideo_UpdateXFB Video_UpdateXFB;
TVideo_BeginField Video_BeginField;
TVideo_AccessEFB Video_AccessEFB;
TVideo_AddMessage Video_AddMessage;

View File

@ -71,6 +71,18 @@
namespace Common
{
// MemFence: Neither the compiler nor the CPU can reorder memory accesses
// beyond this barrier.
__forceinline void MemFence()
{
#ifdef _WIN32
MemoryBarrier();
#else
// TODO: UNIX experts, please implement the memory fence.
#endif
}
class CriticalSection
{
#ifdef _WIN32

View File

@ -987,15 +987,7 @@ void GenerateVIInterrupt(VIInterruptType _VIInterrupt)
}
}
u8* GetXFBPointerTop()
{
if (m_XFBInfoTop.POFF)
return Memory::GetPointer(m_XFBInfoTop.FBB << 5);
else
return Memory::GetPointer(m_XFBInfoTop.FBB);
}
u32 GetXFBPointerTop_GC()
u32 GetXFBAddressTop()
{
if (m_XFBInfoTop.POFF)
return m_XFBInfoTop.FBB << 5;
@ -1003,16 +995,7 @@ u32 GetXFBPointerTop_GC()
return m_XFBInfoTop.FBB;
}
u8* GetXFBPointerBottom()
{
// POFF for XFB bottom is connected to POFF for XFB top
if (m_XFBInfoTop.POFF)
return Memory::GetPointer(m_XFBInfoBottom.FBB << 5);
else
return Memory::GetPointer(m_XFBInfoBottom.FBB);
}
u32 GetXFBPointerBottom_GC()
u32 GetXFBAddressBottom()
{
// POFF for XFB bottom is connected to POFF for XFB top
if (m_XFBInfoTop.POFF)
@ -1058,6 +1041,25 @@ int getTicksPerLine() {
}
static void BeginField(u32 xfbAddr, FieldType field)
{
static const char* const fieldTypeNames[] = { "Progressive", "Upper", "Lower" };
DEBUG_LOG(VIDEOINTERFACE, "(VI->BeginField): addr: %.08X | FieldSteps %u | FbSteps %u | ACV %u | Field %s",
xfbAddr, m_HorizontalStepping.FieldSteps, m_HorizontalStepping.FbSteps, m_VerticalTimingRegister.ACV,
fieldTypeNames[field]
);
u32 fbWidth = m_HorizontalStepping.FieldSteps * 16;
u32 fbHeight = (m_HorizontalStepping.FbSteps / m_HorizontalStepping.FieldSteps) * m_VerticalTimingRegister.ACV;
Common::PluginVideo* video = CPluginManager::GetInstance().GetVideo();
if (xfbAddr && video->IsValid())
{
video->Video_BeginField(xfbAddr, field, fbWidth, fbHeight);
}
}
// Purpose 1: Send VI interrupt for every screen refresh
// Purpose 2: Execute XFB copy in homebrew games
// Run when: This is run 7200 times per second on full speed
@ -1094,57 +1096,32 @@ void Update()
if (m_VBeamPos == NextXFBRender)
{
u32 xfbAddr = 0;
int yOffset = 0;
if (NextXFBRender == 1)
{
NextXFBRender = LinesPerField;
// TODO: proper VI regs typedef and logic for XFB to work.
// eg. Animal Crossing gc have smth in TFBL.XOF bitfield.
// "XOF - Horizontal Offset of the left-most pixel within the first word of the fetched picture."
xfbAddr = GetXFBPointerTop_GC();
u32 xfbAddr = GetXFBAddressTop();
BeginField(xfbAddr, m_DisplayControlRegister.NIN ? FIELD_PROGRESSIVE : FIELD_UPPER);
}
else
{
NextXFBRender = 1;
// Previously checked m_XFBInfoTop.POFF then used m_XFBInfoBottom.FBB, try reverting if there are problems
xfbAddr = GetXFBPointerBottom_GC();
yOffset = -1;
}
Common::PluginVideo* video = CPluginManager::GetInstance().GetVideo();
if (xfbAddr && video->IsValid())
{
int fbWidth = m_HorizontalStepping.FieldSteps * 16;
int fbHeight = (m_HorizontalStepping.FbSteps / m_HorizontalStepping.FieldSteps) * m_VerticalTimingRegister.ACV;
DEBUG_LOG(VIDEOINTERFACE, "(VI->XFBUpdate): ptr: %.08X | %ix%i | xoff: %i",
xfbAddr, fbWidth, fbHeight, m_XFBInfoTop.XOFF);
if (Core::GetStartupParameter().bUseDualCore)
// scheduled on EmuThread in DC mode
video->Video_UpdateXFB(xfbAddr, fbWidth, fbHeight, yOffset, TRUE);
else
// otherwise do it now from here (CPUthread)
video->Video_UpdateXFB(xfbAddr, fbWidth, fbHeight, yOffset, FALSE);
u32 xfbAddr = GetXFBAddressBottom();
BeginField(xfbAddr, m_DisplayControlRegister.NIN ? FIELD_PROGRESSIVE : FIELD_LOWER);
}
}
// check INT_PRERETRACE
if (m_InterruptRegister[0].VCT == m_VBeamPos)
{
m_InterruptRegister[0].IR_INT = 1;
UpdateInterrupts();
}
// INT_POSTRETRACE
if (m_InterruptRegister[1].VCT == m_VBeamPos)
{
m_InterruptRegister[1].IR_INT = 1;
UpdateInterrupts();
}
for (int i = 0; i < 4; ++i)
{
if (m_InterruptRegister[i].VCT == m_VBeamPos)
{
m_InterruptRegister[i].IR_INT = 1;
UpdateInterrupts();
}
}
}
} // namespace

View File

@ -28,8 +28,6 @@
#include "Fifo.h"
// TODO (mb2): move/rm this global
volatile u32 g_XFBUpdateRequested = FALSE;
extern u8* g_pVideoData;
volatile bool g_EFBAccessRequested = false;
@ -63,7 +61,6 @@ void Fifo_Init()
videoBuffer = (u8*)AllocateMemoryPages(FIFO_SIZE);
fifo_exit_event.Init();
fifoStateRun = false;
g_XFBUpdateRequested = FALSE;
}
void Fifo_Shutdown()
@ -147,10 +144,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
#endif
// Draw XFB if CP/GPfifo isn't used
if (g_XFBUpdateRequested)
{
Video_UpdateXFB(NULL, 0, 0, 0, FALSE);
}
VideoFifo_CheckSwapRequest();
if (g_EFBAccessRequested)
{

View File

@ -37,4 +37,8 @@ void Fifo_ExitLoopNonBlocking();
void Fifo_DoState(PointerWrap &f);
// Implemented by the Video Plugin
void VideoFifo_CheckSwapRequest();
void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight);
#endif

View File

@ -5,7 +5,6 @@
#ifndef _VIDEO_H_INCLUDED__
#define _VIDEO_H_INCLUDED__
#include "Thread.h"
#include "PluginSpecs.h"
#include "ExportProlog.h"
@ -23,6 +22,13 @@ typedef void (*TUpdateInterrupts)(void);
typedef void (*TUpdateFPSDisplay)(const char* text); // sets the window title
typedef void (*TKeyPressed)(int keycode, bool shift, bool control); // sets the window title
enum FieldType
{
FIELD_PROGRESSIVE = 0,
FIELD_UPPER,
FIELD_LOWER
};
enum EFBAccessType
{
PEEK_Z = 0,
@ -110,15 +116,14 @@ EXPORT void CALL Video_Prepare(void);
EXPORT void CALL Video_SendFifoData(u8* _uData, u32 len);
// __________________________________________________________________________________________________
// Function: Video_UpdateXFB
// TODO: This DOC IS BROKEN!
// Purpose: This function is called when you have to flip the yuv2
// video-buffer. You should ignore this function after you
// got the first EFB to XFB copy.
// input: pointer to the XFB, width and height of the XFB
// Function: Video_BeginField
// Purpose: When a vertical blank occurs in the VI emulator, this function tells the video plugin
// what the parameters of the upcoming field are. The video plugin should make sure the
// previous field is on the player's display before returning.
// input: vi parameters of the next field
// output: none
//
EXPORT void CALL Video_UpdateXFB(u32 _dwXFBAddr, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, bool scheduling);
EXPORT void CALL Video_BeginField(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight);
// __________________________________________________________________________________________________
// Function: Video_AccessEFB

View File

@ -254,7 +254,15 @@ void Shutdown(void)
DeInit();
}
void Video_UpdateXFB(u32 /*_dwXFBAddr*/, u32 /*_dwWidth*/, u32 /*_dwHeight*/, s32 /*_dwYOffset*/, bool /*scheduling*/)
void VideoFifo_CheckSwapRequest()
{
}
void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
{
}
void Video_BeginField(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight)
{
/*
ConvertXFB(tempBuffer, _pXFB, _dwWidth, _dwHeight);

View File

@ -183,20 +183,20 @@ void FramebufferManager::Shutdown()
m_virtualXFBList.clear();
}
void FramebufferManager::CopyToXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc)
void FramebufferManager::CopyToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc)
{
if (g_Config.bUseXFB)
copyToRealXFB(xfbAddr, dstWidth, dstHeight, sourceRc);
copyToRealXFB(xfbAddr, fbWidth, fbHeight, sourceRc);
else
copyToVirtualXFB(xfbAddr, dstWidth, dstHeight, sourceRc);
copyToVirtualXFB(xfbAddr, fbWidth, fbHeight, sourceRc);
}
const XFBSource* FramebufferManager::GetXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight)
const XFBSource* FramebufferManager::GetXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
{
if (g_Config.bUseXFB)
return getRealXFBSource(xfbAddr, srcWidth, srcHeight);
return getRealXFBSource(xfbAddr, fbWidth, fbHeight);
else
return getVirtualXFBSource(xfbAddr, srcWidth, srcHeight);
return getVirtualXFBSource(xfbAddr, fbWidth, fbHeight);
}
GLuint FramebufferManager::GetEFBColorTexture(const TRectangle& sourceRc) const
@ -283,7 +283,7 @@ FramebufferManager::findVirtualXFB(u32 xfbAddr, u32 width, u32 height)
return m_virtualXFBList.end();
}
void FramebufferManager::copyToRealXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc)
void FramebufferManager::copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc)
{
u8* pXFB = Memory_GetPtr(xfbAddr);
if (!pXFB)
@ -292,22 +292,22 @@ void FramebufferManager::copyToRealXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight,
return;
}
XFB_Write(pXFB, sourceRc, dstWidth, dstHeight);
XFB_Write(pXFB, sourceRc, fbWidth, fbHeight);
}
void FramebufferManager::copyToVirtualXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc)
void FramebufferManager::copyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc)
{
GLuint xfbTexture;
VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, dstWidth, dstHeight);
VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, fbWidth, fbHeight);
if (it != m_virtualXFBList.end())
{
// Overwrite an existing Virtual XFB.
it->xfbAddr = xfbAddr;
it->xfbWidth = dstWidth;
it->xfbHeight = dstHeight;
it->xfbWidth = fbWidth;
it->xfbHeight = fbHeight;
it->xfbSource.texWidth = m_targetWidth;
it->xfbSource.texHeight = m_targetHeight;
@ -342,8 +342,8 @@ void FramebufferManager::copyToVirtualXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeig
VirtualXFB newVirt;
newVirt.xfbAddr = xfbAddr;
newVirt.xfbWidth = dstWidth;
newVirt.xfbHeight = dstHeight;
newVirt.xfbWidth = fbWidth;
newVirt.xfbHeight = fbHeight;
newVirt.xfbSource.texture = xfbTexture;
newVirt.xfbSource.texWidth = m_targetWidth;
@ -405,15 +405,15 @@ void FramebufferManager::copyToVirtualXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeig
}
}
const XFBSource* FramebufferManager::getRealXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight)
const XFBSource* FramebufferManager::getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
{
m_realXFBSource.texWidth = XFB_WIDTH;
m_realXFBSource.texHeight = XFB_HEIGHT;
m_realXFBSource.sourceRc.left = 0;
m_realXFBSource.sourceRc.top = 0;
m_realXFBSource.sourceRc.right = srcWidth;
m_realXFBSource.sourceRc.bottom = srcHeight;
m_realXFBSource.sourceRc.right = fbWidth;
m_realXFBSource.sourceRc.bottom = fbHeight;
if (!m_realXFBSource.texture)
{
@ -426,12 +426,12 @@ const XFBSource* FramebufferManager::getRealXFBSource(u32 xfbAddr, u32 srcWidth,
}
// Decode YUYV data from GameCube RAM
TextureConverter::DecodeToTexture(xfbAddr, srcWidth, srcHeight, m_realXFBSource.texture);
TextureConverter::DecodeToTexture(xfbAddr, fbWidth, fbHeight, m_realXFBSource.texture);
return &m_realXFBSource;
}
const XFBSource* FramebufferManager::getVirtualXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight)
const XFBSource* FramebufferManager::getVirtualXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
{
if (m_virtualXFBList.size() == 0)
{
@ -439,7 +439,7 @@ const XFBSource* FramebufferManager::getVirtualXFBSource(u32 xfbAddr, u32 srcWid
return NULL;
}
VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, srcWidth, srcHeight);
VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, fbWidth, fbHeight);
if (it == m_virtualXFBList.end())
{
// Virtual XFB is not in the list, so return the most recently rendered

View File

@ -97,9 +97,9 @@ public:
// sourceRc is in GL target coordinates, not GameCube EFB coordinates!
// TODO: Clean that up.
void CopyToXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc);
void CopyToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc);
const XFBSource* GetXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight);
const XFBSource* GetXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight);
// To get the EFB in texture form, these functions may have to transfer
// the EFB to a resolved texture first.
@ -124,10 +124,10 @@ private:
VirtualXFBListType::iterator findVirtualXFB(u32 xfbAddr, u32 width, u32 height);
void copyToRealXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc);
void copyToVirtualXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc);
const XFBSource* getRealXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight);
const XFBSource* getVirtualXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight);
void copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc);
void copyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc);
const XFBSource* getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight);
const XFBSource* getVirtualXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight);
int m_targetWidth;
int m_targetHeight;

View File

@ -55,6 +55,7 @@
#include "Timer.h"
#include "StringUtil.h"
#include "FramebufferManager.h"
#include "Fifo.h"
#include "main.h" // Local
#ifdef _WIN32
@ -660,39 +661,19 @@ void ComputeBackbufferRectangle(TRectangle *rc)
rc->bottom = YOffset + ceil(FloatGLHeight);
}
// TODO: Use something less ugly than an Evil Global Variable.
// Also, protect this structure with a mutex.
extern volatile struct // Comes from main.cpp
{
u32 xfbAddr;
u32 width;
u32 height;
s32 yOffset;
} tUpdateXFBArgs;
void Renderer::RenderToXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc)
void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc)
{
u32 aLower = xfbAddr;
u32 aUpper = xfbAddr + 2 * dstWidth * dstHeight;
u32 bLower = tUpdateXFBArgs.xfbAddr;
u32 bUpper = tUpdateXFBArgs.xfbAddr + 2 * tUpdateXFBArgs.width * tUpdateXFBArgs.height;
// If we're about to write into a requested XFB, make sure the previous
// If we're about to write to a requested XFB, make sure the previous
// contents make it to the screen first.
if (g_XFBUpdateRequested && addrRangesOverlap(aLower, aUpper, bLower, bUpper))
{
Video_UpdateXFB(NULL, 0, 0, 0, FALSE);
}
VideoFifo_CheckSwapRequestAt(xfbAddr, fbWidth, fbHeight);
s_framebufferManager.CopyToXFB(xfbAddr, dstWidth, dstHeight, sourceRc);
s_framebufferManager.CopyToXFB(xfbAddr, fbWidth, fbHeight, sourceRc);
}
// This function has the final picture. We adjust the aspect ratio here.
// yOffset is used to eliminate interlacing jitter in Real XFB mode.
void Renderer::Swap(u32 xfbAddr, u32 srcWidth, u32 srcHeight, s32 yOffset)
void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight)
{
const XFBSource* xfbSource = s_framebufferManager.GetXFBSource(xfbAddr, srcWidth, srcHeight);
const XFBSource* xfbSource = s_framebufferManager.GetXFBSource(xfbAddr, fbWidth, fbHeight);
if (!xfbSource)
{
WARN_LOG(VIDEO, "Failed to get video for this frame");
@ -724,6 +705,7 @@ void Renderer::Swap(u32 xfbAddr, u32 srcWidth, u32 srcHeight, s32 yOffset)
v_max = (float)xfbSource->texHeight;
}
int yOffset = (g_Config.bUseXFB && field == FIELD_LOWER) ? -1 : 0;
v_min -= yOffset;
v_max -= yOffset;

View File

@ -87,10 +87,10 @@ public:
static void FlipImageData(u8 *data, int w, int h);
static bool SaveRenderTarget(const char *filename, int w, int h, int YOffset = 0);
static void RenderToXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc);
static void RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc);
// Finish up the current frame, print some stats
static void Swap(u32 xfbAddr, u32 srcWidth, u32 srcHeight, s32 yOffset);
static void Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight);
};
void ComputeBackbufferRectangle(TRectangle *rc);

View File

@ -99,6 +99,9 @@ int GLScissorX, GLScissorY, GLScissorW, GLScissorH;
static bool s_PluginInitialized = false;
static bool s_swapRequested = false;
static Common::Event s_swapResponseEvent;
static volatile u32 s_AccessEFBResult = 0, s_EFBx, s_EFBy;
static volatile EFBAccessType s_AccessEFBType;
static Common::Event s_AccessEFBDone;
@ -372,6 +375,10 @@ void Video_Prepare(void)
VertexLoaderManager::Init();
TextureConverter::Init();
s_swapRequested = false;
s_swapResponseEvent.Init();
s_swapResponseEvent.Set();
s_PluginInitialized = true;
INFO_LOG(VIDEO, "Video plugin initialized.");
}
@ -380,6 +387,9 @@ void Shutdown(void)
{
s_PluginInitialized = false;
s_swapRequested = false;
s_swapResponseEvent.Shutdown();
Fifo_Shutdown();
PostProcessing::Shutdown();
TextureConverter::Shutdown();
@ -423,48 +433,75 @@ void Video_AddMessage(const char* pstr, u32 milliseconds)
OSD::AddMessage(pstr, milliseconds);
}
// TODO: Protect this structure with a mutex.
volatile struct
{
static volatile struct
{
u32 xfbAddr;
u32 width;
u32 height;
s32 yOffset;
} tUpdateXFBArgs;
FieldType field;
u32 fbWidth;
u32 fbHeight;
} s_beginFieldArgs = { 0 };
// Run from the CPU thread (from VideoInterface.cpp) for certain homebrew games only
void Video_UpdateXFB(u32 _dwXFBAddr, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, bool scheduling)
// Run from the graphics thread (from Fifo.cpp)
void VideoFifo_CheckSwapRequest()
{
if (s_swapRequested)
{
s_swapRequested = false;
Common::MemFence();
Renderer::Swap(s_beginFieldArgs.xfbAddr, s_beginFieldArgs.field, s_beginFieldArgs.fbWidth, s_beginFieldArgs.fbHeight);
// TODO: Find better name for this because I don't know if it means what it says.
g_VideoInitialize.pCopiedToXFB();
s_swapResponseEvent.Set();
}
}
inline bool addrRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper)
{
return (
(aLower >= bLower && aLower < bUpper) ||
(aUpper >= bLower && aUpper < bUpper) ||
(bLower >= aLower && bLower < aUpper) ||
(bUpper >= aLower && bUpper < aUpper)
);
}
// Run from the graphics thread (from Fifo.cpp)
void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
{
if (s_swapRequested)
{
u32 aLower = xfbAddr;
u32 aUpper = xfbAddr + 2 * fbWidth * fbHeight;
Common::MemFence();
u32 bLower = s_beginFieldArgs.xfbAddr;
u32 bUpper = s_beginFieldArgs.xfbAddr + 2 * s_beginFieldArgs.fbWidth * s_beginFieldArgs.fbHeight;
if (addrRangesOverlap(aLower, aUpper, bLower, bUpper))
VideoFifo_CheckSwapRequest();
}
}
// Run from the CPU thread (from VideoInterface.cpp)
void Video_BeginField(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight)
{
if (s_PluginInitialized)
{
if (scheduling) // From CPU in DC mode
{
tUpdateXFBArgs.xfbAddr = _dwXFBAddr;
tUpdateXFBArgs.width = _dwWidth;
tUpdateXFBArgs.height = _dwHeight;
tUpdateXFBArgs.yOffset = _dwYOffset;
s_swapResponseEvent.MsgWait();
g_XFBUpdateRequested = TRUE;
}
else // From CPU in SC mode or graphics thread in DC mode
{
g_XFBUpdateRequested = FALSE;
s_beginFieldArgs.xfbAddr = xfbAddr;
s_beginFieldArgs.field = field;
s_beginFieldArgs.fbWidth = fbWidth;
s_beginFieldArgs.fbHeight = fbHeight;
if (!_dwXFBAddr)
{
// From graphics thread in DC mode
_dwXFBAddr = tUpdateXFBArgs.xfbAddr;
_dwWidth = tUpdateXFBArgs.width;
_dwHeight = tUpdateXFBArgs.height;
_dwYOffset = tUpdateXFBArgs.yOffset;
}
Common::MemFence();
// TODO: Use real XFB source parameters based on VI settings
Renderer::Swap(_dwXFBAddr, _dwWidth, _dwHeight, g_Config.bUseXFB ? _dwYOffset : 0);
g_VideoInitialize.pCopiedToXFB();
}
s_swapRequested = true;
}
}