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

View File

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

View File

@ -71,6 +71,18 @@
namespace Common 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 class CriticalSection
{ {
#ifdef _WIN32 #ifdef _WIN32

View File

@ -987,15 +987,7 @@ void GenerateVIInterrupt(VIInterruptType _VIInterrupt)
} }
} }
u8* GetXFBPointerTop() u32 GetXFBAddressTop()
{
if (m_XFBInfoTop.POFF)
return Memory::GetPointer(m_XFBInfoTop.FBB << 5);
else
return Memory::GetPointer(m_XFBInfoTop.FBB);
}
u32 GetXFBPointerTop_GC()
{ {
if (m_XFBInfoTop.POFF) if (m_XFBInfoTop.POFF)
return m_XFBInfoTop.FBB << 5; return m_XFBInfoTop.FBB << 5;
@ -1003,16 +995,7 @@ u32 GetXFBPointerTop_GC()
return m_XFBInfoTop.FBB; return m_XFBInfoTop.FBB;
} }
u8* GetXFBPointerBottom() u32 GetXFBAddressBottom()
{
// 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()
{ {
// POFF for XFB bottom is connected to POFF for XFB top // POFF for XFB bottom is connected to POFF for XFB top
if (m_XFBInfoTop.POFF) 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 1: Send VI interrupt for every screen refresh
// Purpose 2: Execute XFB copy in homebrew games // Purpose 2: Execute XFB copy in homebrew games
// Run when: This is run 7200 times per second on full speed // Run when: This is run 7200 times per second on full speed
@ -1094,57 +1096,32 @@ void Update()
if (m_VBeamPos == NextXFBRender) if (m_VBeamPos == NextXFBRender)
{ {
u32 xfbAddr = 0;
int yOffset = 0;
if (NextXFBRender == 1) if (NextXFBRender == 1)
{ {
NextXFBRender = LinesPerField; NextXFBRender = LinesPerField;
// TODO: proper VI regs typedef and logic for XFB to work. // TODO: proper VI regs typedef and logic for XFB to work.
// eg. Animal Crossing gc have smth in TFBL.XOF bitfield. // 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." // "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 else
{ {
NextXFBRender = 1; NextXFBRender = 1;
// Previously checked m_XFBInfoTop.POFF then used m_XFBInfoBottom.FBB, try reverting if there are problems // Previously checked m_XFBInfoTop.POFF then used m_XFBInfoBottom.FBB, try reverting if there are problems
xfbAddr = GetXFBPointerBottom_GC(); u32 xfbAddr = GetXFBAddressBottom();
yOffset = -1; BeginField(xfbAddr, m_DisplayControlRegister.NIN ? FIELD_PROGRESSIVE : FIELD_LOWER);
}
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);
} }
} }
// check INT_PRERETRACE for (int i = 0; i < 4; ++i)
if (m_InterruptRegister[0].VCT == m_VBeamPos) {
{ if (m_InterruptRegister[i].VCT == m_VBeamPos)
m_InterruptRegister[0].IR_INT = 1; {
UpdateInterrupts(); m_InterruptRegister[i].IR_INT = 1;
} UpdateInterrupts();
}
// INT_POSTRETRACE }
if (m_InterruptRegister[1].VCT == m_VBeamPos)
{
m_InterruptRegister[1].IR_INT = 1;
UpdateInterrupts();
}
} }
} // namespace } // namespace

View File

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

View File

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

View File

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

View File

@ -254,7 +254,15 @@ void Shutdown(void)
DeInit(); 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); ConvertXFB(tempBuffer, _pXFB, _dwWidth, _dwHeight);

View File

@ -183,20 +183,20 @@ void FramebufferManager::Shutdown()
m_virtualXFBList.clear(); 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) if (g_Config.bUseXFB)
copyToRealXFB(xfbAddr, dstWidth, dstHeight, sourceRc); copyToRealXFB(xfbAddr, fbWidth, fbHeight, sourceRc);
else 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) if (g_Config.bUseXFB)
return getRealXFBSource(xfbAddr, srcWidth, srcHeight); return getRealXFBSource(xfbAddr, fbWidth, fbHeight);
else else
return getVirtualXFBSource(xfbAddr, srcWidth, srcHeight); return getVirtualXFBSource(xfbAddr, fbWidth, fbHeight);
} }
GLuint FramebufferManager::GetEFBColorTexture(const TRectangle& sourceRc) const GLuint FramebufferManager::GetEFBColorTexture(const TRectangle& sourceRc) const
@ -283,7 +283,7 @@ FramebufferManager::findVirtualXFB(u32 xfbAddr, u32 width, u32 height)
return m_virtualXFBList.end(); 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); u8* pXFB = Memory_GetPtr(xfbAddr);
if (!pXFB) if (!pXFB)
@ -292,22 +292,22 @@ void FramebufferManager::copyToRealXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight,
return; 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; GLuint xfbTexture;
VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, dstWidth, dstHeight); VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, fbWidth, fbHeight);
if (it != m_virtualXFBList.end()) if (it != m_virtualXFBList.end())
{ {
// Overwrite an existing Virtual XFB. // Overwrite an existing Virtual XFB.
it->xfbAddr = xfbAddr; it->xfbAddr = xfbAddr;
it->xfbWidth = dstWidth; it->xfbWidth = fbWidth;
it->xfbHeight = dstHeight; it->xfbHeight = fbHeight;
it->xfbSource.texWidth = m_targetWidth; it->xfbSource.texWidth = m_targetWidth;
it->xfbSource.texHeight = m_targetHeight; it->xfbSource.texHeight = m_targetHeight;
@ -342,8 +342,8 @@ void FramebufferManager::copyToVirtualXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeig
VirtualXFB newVirt; VirtualXFB newVirt;
newVirt.xfbAddr = xfbAddr; newVirt.xfbAddr = xfbAddr;
newVirt.xfbWidth = dstWidth; newVirt.xfbWidth = fbWidth;
newVirt.xfbHeight = dstHeight; newVirt.xfbHeight = fbHeight;
newVirt.xfbSource.texture = xfbTexture; newVirt.xfbSource.texture = xfbTexture;
newVirt.xfbSource.texWidth = m_targetWidth; 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.texWidth = XFB_WIDTH;
m_realXFBSource.texHeight = XFB_HEIGHT; m_realXFBSource.texHeight = XFB_HEIGHT;
m_realXFBSource.sourceRc.left = 0; m_realXFBSource.sourceRc.left = 0;
m_realXFBSource.sourceRc.top = 0; m_realXFBSource.sourceRc.top = 0;
m_realXFBSource.sourceRc.right = srcWidth; m_realXFBSource.sourceRc.right = fbWidth;
m_realXFBSource.sourceRc.bottom = srcHeight; m_realXFBSource.sourceRc.bottom = fbHeight;
if (!m_realXFBSource.texture) if (!m_realXFBSource.texture)
{ {
@ -426,12 +426,12 @@ const XFBSource* FramebufferManager::getRealXFBSource(u32 xfbAddr, u32 srcWidth,
} }
// Decode YUYV data from GameCube RAM // 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; 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) if (m_virtualXFBList.size() == 0)
{ {
@ -439,7 +439,7 @@ const XFBSource* FramebufferManager::getVirtualXFBSource(u32 xfbAddr, u32 srcWid
return NULL; return NULL;
} }
VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, srcWidth, srcHeight); VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, fbWidth, fbHeight);
if (it == m_virtualXFBList.end()) if (it == m_virtualXFBList.end())
{ {
// Virtual XFB is not in the list, so return the most recently rendered // 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! // sourceRc is in GL target coordinates, not GameCube EFB coordinates!
// TODO: Clean that up. // 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 // To get the EFB in texture form, these functions may have to transfer
// the EFB to a resolved texture first. // the EFB to a resolved texture first.
@ -124,10 +124,10 @@ private:
VirtualXFBListType::iterator findVirtualXFB(u32 xfbAddr, u32 width, u32 height); VirtualXFBListType::iterator findVirtualXFB(u32 xfbAddr, u32 width, u32 height);
void copyToRealXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc); void copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc);
void copyToVirtualXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc); void copyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc);
const XFBSource* getRealXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight); const XFBSource* getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight);
const XFBSource* getVirtualXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight); const XFBSource* getVirtualXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight);
int m_targetWidth; int m_targetWidth;
int m_targetHeight; int m_targetHeight;

View File

@ -55,6 +55,7 @@
#include "Timer.h" #include "Timer.h"
#include "StringUtil.h" #include "StringUtil.h"
#include "FramebufferManager.h" #include "FramebufferManager.h"
#include "Fifo.h"
#include "main.h" // Local #include "main.h" // Local
#ifdef _WIN32 #ifdef _WIN32
@ -660,39 +661,19 @@ void ComputeBackbufferRectangle(TRectangle *rc)
rc->bottom = YOffset + ceil(FloatGLHeight); rc->bottom = YOffset + ceil(FloatGLHeight);
} }
// TODO: Use something less ugly than an Evil Global Variable. void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const TRectangle& sourceRc)
// 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)
{ {
u32 aLower = xfbAddr; // If we're about to write to a requested XFB, make sure the previous
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
// contents make it to the screen first. // contents make it to the screen first.
if (g_XFBUpdateRequested && addrRangesOverlap(aLower, aUpper, bLower, bUpper)) VideoFifo_CheckSwapRequestAt(xfbAddr, fbWidth, fbHeight);
{
Video_UpdateXFB(NULL, 0, 0, 0, FALSE);
}
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. // 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, FieldType field, u32 fbWidth, u32 fbHeight)
void Renderer::Swap(u32 xfbAddr, u32 srcWidth, u32 srcHeight, s32 yOffset)
{ {
const XFBSource* xfbSource = s_framebufferManager.GetXFBSource(xfbAddr, srcWidth, srcHeight); const XFBSource* xfbSource = s_framebufferManager.GetXFBSource(xfbAddr, fbWidth, fbHeight);
if (!xfbSource) if (!xfbSource)
{ {
WARN_LOG(VIDEO, "Failed to get video for this frame"); 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; v_max = (float)xfbSource->texHeight;
} }
int yOffset = (g_Config.bUseXFB && field == FIELD_LOWER) ? -1 : 0;
v_min -= yOffset; v_min -= yOffset;
v_max -= yOffset; v_max -= yOffset;

View File

@ -87,10 +87,10 @@ public:
static void FlipImageData(u8 *data, int w, int h); static void FlipImageData(u8 *data, int w, int h);
static bool SaveRenderTarget(const char *filename, int w, int h, int YOffset = 0); 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 // 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); void ComputeBackbufferRectangle(TRectangle *rc);

View File

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