#include "Core/Asura.h"

#ifdef ASURA_PS2_DEFERRED_RENDERING

#include "Rendering/Asura_PS2_DeferredRendering.h"

#include <eekernel.h>
#include <libgraph.h>

#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Logfile.h"

#include "Core/Asura_PS2_DMA.h"
#include "Rendering/Asura_PS2_GS.h"

// M A C R O S ///////////////////////////////////////////////////////////////

// Number of NOP in Path 3 open/close = 16QWs to flush GIF FIFO + 8QWs per active DMA channel (not including GIF dma)
// (MP - 28.07.2003) According to SCEE's George Bain, 24 QWs is enough. See
// the "MskPath3 tip" thread in sce.dev.prog.gs.
//														GIF FIFO   VIF
#define ASURA_PS2_DEFERRED_RENDERING__MSKPATH3_NOP_QWS	(	16	+	8	)
static const Asura_Char* DEFERREDLOGFILE = "Logfiles\\Deferred.txt";

#define GUARD_DMA_CHANNEL_1_QWC_WRITES 0

#if (GUARD_DMA_CHANNEL_1_QWC_WRITES)
	#include "Debugging/Asura_PS2_Breakpoint.h"
	#define DMA_CH1_GUARDED		Asura_PS2_Breakpoint::SetDataBreakpoint( 0x10009020, 0xfffffffc );
	#define DMA_CH1_UNGUARDED	Asura_PS2_Breakpoint::ClearDataBreakpoint();
#else
	#define DMA_CH1_GUARDED		{;}
	#define DMA_CH1_UNGUARDED	{;}
#endif

#define VIF_BUFFER_QTY      (1 << 3)             // VIF Buffer Quantity = 2^3 = 8
#define VIF_BUFFER_QTY_MASK (VIF_BUFFER_QTY - 1) // VIF Buffer Quantity Mask = 7
#define GIF_BUFFER_QTY      (1 << 1)             // GIF Buffer Quantity = 2^1 = 2
#define GIF_BUFFER_QTY_MASK (GIF_BUFFER_QTY - 1) // GIF Buffer Quantity Mask = 1

// C O N S T A N T S /////////////////////////////////////////////////////////

#ifndef ASURA_PS2_SONY_SUBMISSION

static const char* OUT_OF_VIF_BFR_MSG = "ERROR: Out of VIF buffers.\n"
                                        "       This frame is using more VIF buffer space than is available in total.\n"
                                        "       You need to increase your VIF buffer size.\n";

static const char* VIF_BFR_STALL_MSG = "WARNING: Deferred rendering forced CPU stall.\n"
                                       "         You should consider increasing your VIF buffer size.\n";

#endif // #ifndef ASURA_PS2_SONY_SUBMISSION

// G L O B A L S /////////////////////////////////////////////////////////////

static bool g_bInitialised = false;

static Asura_PS2_QW* g_pxVifBuffers[VIF_BUFFER_QTY]; // A pool of vif buffers which get used as they are needed (1 whole frames worth of drawing must fit in a the number of buffers -1 )
static Asura_PS2_QW* g_pxGifBuffers[GIF_BUFFER_QTY]; // The two gif buffers

#if _DEBUG
static u_int g_uLastVIFBfrQWCReturned = 0;
#endif

#if _DEBUG
// If set to anything other than -1, the buffer at this index will be
// repeatedly redrawn.
static bool dbg_bDeferredRenderingStopUpdatingRenderList = false;

// Used when dbg_bDeferredRenderingStopUpdatingRenderList is on.
static bool  g_bGotBfrsToRedraw = false;
static u_int g_uFirstVifBfrToRedraw;
static u_int g_uLastVifBfrToRedraw;
static u_int g_uGifBfrToRedraw;
#endif

DEBUGVAR bool dbg_bDeferredRenderingNeverOpenPATH3 = false;

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

Asura_PS2_QW*  Asura_PS2_DeferredRendering::s_pxVifPtr;						// The current Vif buffer update point
Asura_PS2_QW*  Asura_PS2_DeferredRendering::s_pxGifPtr;						// The current Gif buffer update point
Asura_PS2_QW*  Asura_PS2_DeferredRendering::s_pxVifEnd;						// The end of the current vif buffer
Asura_PS2_QW*  Asura_PS2_DeferredRendering::s_pxGifEnd;						// The end of the current Gif buffer
Asura_PS2_QW*  Asura_PS2_DeferredRendering::s_pxPath3Gate;					// The Path 3 Mask open/close drawlist
Asura_PS2_QW*  Asura_PS2_DeferredRendering::s_pxLastPath3Gate;				// The last point in the vif buffer where a mask past 3 open/close was added
Asura_PS2_QW*  Asura_PS2_DeferredRendering::s_pxMaskPath3;					// A miniture DMA block to switch on/off mask path3
u_int		   Asura_PS2_DeferredRendering::s_uCurrentGifBuffer;			// The current GIF buffer being built
volatile u_int Asura_PS2_DeferredRendering::sv_uBuildingBuffer;				// The current VIF buffer being built
volatile u_int Asura_PS2_DeferredRendering::sv_uBufferDrawing;				// The current buffer being drawn
volatile u_int Asura_PS2_DeferredRendering::sv_uLastBufferToDraw;			// The last buffer that still needs to be drawn this frame
int			   Asura_PS2_DeferredRendering::s_iVIF1DmacHandlerID;			// The VIF1 DMAC intterupt handler

#ifdef ASURA_PS2_DEFERRED_RENDERING__TRACK_BUFFER_USAGE
static u_int g_uMaxGIFBufferUsage  = 0;
static u_int g_uMaxVIF1BufferUsage = 0;
#endif

void Asura_PS2_DeferredRendering::Initialise( void )
{
	// Reset memory high water marks
#ifdef ASURA_PS2_DEFERRED_RENDERING__TRACK_BUFFER_USAGE
	g_uMaxGIFBufferUsage  = 0;
	g_uMaxVIF1BufferUsage = 0;
#endif

	// Set current buffer
	s_uCurrentGifBuffer  = 0;
	sv_uBuildingBuffer	 = 0;
	sv_uBufferDrawing	 = VIF_BUFFER_QTY_MASK;
	sv_uLastBufferToDraw = VIF_BUFFER_QTY_MASK;

	// VIF Buffers first
    {
        // Size should be a multiple of 128 bytes.
        Asura_Assert( !(ASURA_PS2_DEFERRED_RENDERING__VIF_BUFFER_SIZE & 127), "" );

        const u_int uTotalSize = ASURA_PS2_DEFERRED_RENDERING__VIF_BUFFER_SIZE * VIF_BUFFER_QTY;
        u_char* pucNextBfr = static_cast<u_char*>( Asura_AllocateAlign(uTotalSize, ASURA_ALIGN_128_BYTE) );

	    for( u_int u = 0; u < VIF_BUFFER_QTY; ++u )
	    {
		    Asura_Assert( !g_pxVifBuffers[u], "" );
            g_pxVifBuffers[u] = Asura_PS2_UncachedAcceleratedMem( reinterpret_cast<Asura_PS2_QW*>(pucNextBfr) );
            pucNextBfr += ASURA_PS2_DEFERRED_RENDERING__VIF_BUFFER_SIZE;
        }
    }

	// Now Gif Buffers
    {
        // Size should be a multiple of 128 bytes.
        Asura_Assert( !(ASURA_PS2_DEFERRED_RENDERING__GIF_BUFFER_SIZE & 127), "" );

        const u_int uTotalSize = ASURA_PS2_DEFERRED_RENDERING__GIF_BUFFER_SIZE * GIF_BUFFER_QTY;
        u_char* pucNextBfr = static_cast<u_char*>( Asura_AllocateAlign(uTotalSize, ASURA_ALIGN_128_BYTE) );

	    for( u_int u = 0; u < GIF_BUFFER_QTY; ++u )
	    {
		    Asura_Assert( !g_pxGifBuffers[u], "" );
            g_pxGifBuffers[u] = Asura_PS2_UncachedAcceleratedMem( reinterpret_cast<Asura_PS2_QW*>(pucNextBfr) );
            pucNextBfr += ASURA_PS2_DEFERRED_RENDERING__GIF_BUFFER_SIZE;
        }
	}

	// Ensure we don't already have any buffers allocated yet
	Asura_Assert( s_pxPath3Gate		== NULL, "Asura_PS2_DeferredRendering::Initialise() called more than once" );
	Asura_Assert( s_pxMaskPath3		== NULL, "Asura_PS2_DeferredRendering::Initialise() called more than once" );
	
	// Allocate memory for the gif buffers
	s_pxPath3Gate	  = reinterpret_cast<Asura_PS2_QW*>(Asura_AllocateAlign( (((ASURA_PS2_DEFERRED_RENDERING__MSKPATH3_NOP_QWS+2)<<4)+63)&(~63), ASURA_ALIGN_64_BYTE ));
	s_pxMaskPath3	  = reinterpret_cast<Asura_PS2_QW*>(Asura_AllocateAlign( ((1<<4)+63)&(~63), ASURA_ALIGN_64_BYTE ));

	// Make sure we allocated them all ok
	Asura_Assert( s_pxPath3Gate		!= NULL, "Asura_PS2_DeferredRendering::Initialise() - failed to allocate Path 3 gate buffer" );
	Asura_Assert( s_pxMaskPath3		!= NULL, "Asura_PS2_DeferredRendering::Initialise() - failed to allocate Path 3 gate buffer" );

	// All pointer we are writting through are kept uncached
	s_pxPath3Gate	  = Asura_PS2_UncachedAcceleratedMem( s_pxPath3Gate	 );
	s_pxMaskPath3	  = Asura_PS2_UncachedAcceleratedMem( s_pxMaskPath3	 );

	// Create the single mask path3 DMA block
	s_pxMaskPath3->u32s[0] = PY_DMA_END | 0;
	s_pxMaskPath3->u32s[1] = 0;
	s_pxMaskPath3->u32s[2] = SCE_VIF1_SET_MSKPATH3( 0, 0 );
	s_pxMaskPath3->u32s[3] = 0;

	// Create MskPath3 Gate open/close dma chain
	s_pxPath3Gate[0].u32s[0] = PY_DMA_RET | (ASURA_PS2_DEFERRED_RENDERING__MSKPATH3_NOP_QWS+1);
	s_pxPath3Gate[0].u32s[1] = 0;
	// Disable MskPath3 - (Open path 3)
	s_pxPath3Gate[0].u32s[2] = SCE_VIF1_SET_MSKPATH3( 0x0000, 0 );
	s_pxPath3Gate[0].u32s[3] = SCE_VIF1_SET_NOP( false );

	// Now add 24 qws of NOP
	for (u_int u = 1; u<=ASURA_PS2_DEFERRED_RENDERING__MSKPATH3_NOP_QWS; u++)
	{
		s_pxPath3Gate[u].u128 = 0x0L;
	}
	
	// Enable MskPath3 - (Close Path 3)
	s_pxPath3Gate[ASURA_PS2_DEFERRED_RENDERING__MSKPATH3_NOP_QWS+1].u32s[0] = SCE_VIF1_SET_NOP( false );
	s_pxPath3Gate[ASURA_PS2_DEFERRED_RENDERING__MSKPATH3_NOP_QWS+1].u32s[1] = SCE_VIF1_SET_NOP( false );
	s_pxPath3Gate[ASURA_PS2_DEFERRED_RENDERING__MSKPATH3_NOP_QWS+1].u32s[2] = SCE_VIF1_SET_NOP( false );
	s_pxPath3Gate[ASURA_PS2_DEFERRED_RENDERING__MSKPATH3_NOP_QWS+1].u32s[3] = SCE_VIF1_SET_MSKPATH3( 0x8000, 0 );

	// No more writting done through the path3 pointer, so make it a cached pointer ready for adding to draw lists
	s_pxPath3Gate = Asura_PS2_CachedMem( s_pxPath3Gate );

	// Set GIF to IMT mode
	*GIF_MODE = ((0 << GIF_MODE_M3R_O) | (1 << GIF_MODE_IMT_O));

	// Register our interrupt handler, which will handle the swapping of upload buffers
	s_iVIF1DmacHandlerID = AddDmacHandler( DMAC_VIF1, BufferSwapInteruptHandler, 0 );

	// Reset the chains vif and gif pointers
	s_pxVifPtr = g_pxVifBuffers[sv_uBuildingBuffer];
	s_pxGifPtr = g_pxGifBuffers[s_uCurrentGifBuffer];

	// and the end of buffer pointers - leaving space to add the end tags
	s_pxVifEnd = s_pxVifPtr + (ASURA_PS2_DEFERRED_RENDERING__VIF_BUFFER_SIZE>>4) - 1;
	s_pxGifEnd = s_pxGifPtr + (ASURA_PS2_DEFERRED_RENDERING__GIF_BUFFER_SIZE>>4) - 1;

	// Add the initial path3 open/close
	AddPath3Gate();

    g_bInitialised = true;
}

void Asura_PS2_DeferredRendering::Shutdown( void )
{
	// Finish the last deferred render block now
	WaitForDeferredRendering();

	// Remove our interrupt handler
	RemoveDmacHandler( DMAC_VIF1, s_iVIF1DmacHandlerID );

	// Deallocate the vif buffers
    Asura_DeallocateAlign( Asura_PS2_CachedMem(g_pxVifBuffers[0]) );
	for( u_int u = 0; u < VIF_BUFFER_QTY; ++u )
	{
		g_pxVifBuffers[u] = NULL;
	}

	// And the gif buffers
    Asura_DeallocateAlign( Asura_PS2_CachedMem(g_pxGifBuffers[0]) );
	for( u_int u = 0; u < GIF_BUFFER_QTY; ++u )
	{
		g_pxGifBuffers[u] = NULL;
	}

	// And the path 3 buffers
	if ( s_pxPath3Gate )
	{
		s_pxPath3Gate = Asura_PS2_CachedMem( s_pxPath3Gate );
		Asura_DeallocateAlign( s_pxPath3Gate );
		s_pxPath3Gate = NULL;
	}
	if ( s_pxMaskPath3 )
	{
		s_pxMaskPath3 = Asura_PS2_CachedMem( s_pxMaskPath3 );
		Asura_DeallocateAlign( s_pxMaskPath3 );
		s_pxMaskPath3 = NULL;
	}

	// Reset the pointers
	s_pxVifEnd		  = NULL;
	s_pxGifEnd		  = NULL;
	s_pxVifPtr		  = NULL;
	s_pxGifPtr		  = NULL;
	s_pxLastPath3Gate = NULL;
	s_pxMaskPath3	  = NULL;

    g_bInitialised = false;
}

void Asura_PS2_DeferredRendering::Reset( void )
{
    if( !g_bInitialised ) { return; }

	// Wait for the current render to finish
	WaitForDeferredRendering();

	// Reset memory high water marks
#ifdef ASURA_PS2_DEFERRED_RENDERING__TRACK_BUFFER_USAGE
	g_uMaxGIFBufferUsage  = 0;
	g_uMaxVIF1BufferUsage = 0;
#endif

	// Set current buffer
	s_uCurrentGifBuffer  = 0;
	sv_uBuildingBuffer	 = 0;
	sv_uBufferDrawing	 = VIF_BUFFER_QTY_MASK;
	sv_uLastBufferToDraw = VIF_BUFFER_QTY_MASK;

	// Add end tags too all the VIF dma buffers in case any of them get sent
	for ( u_int u=0; u<VIF_BUFFER_QTY; u++ )
	{
		g_pxVifBuffers[u]->u32s[0] = PY_DMA_END | 0;		// DMA::End 
		g_pxVifBuffers[u]->u32s[1] = 0;
		g_pxVifBuffers[u]->u32s[2] = SCE_VIF1_SET_MSKPATH3( 0, 0 );		// Make sure path 3 is unmasked
		g_pxVifBuffers[u]->u32s[3] = SCE_VIF1_SET_NOP( false );
	}

	// And the GIF DMA buffers
	for ( u_int u=0; u<GIF_BUFFER_QTY; u++ )
	{
		g_pxGifBuffers[u]->u32s[0] = PY_DMA_END | 0;		// DMA::End 
		g_pxGifBuffers[u]->u32s[1] = 0;
		g_pxGifBuffers[u]->u32s[2] = 0;
		g_pxGifBuffers[u]->u32s[3] = 0;
	}

	// Reset the chains vif and gif pointers
	s_pxVifPtr = g_pxVifBuffers[sv_uBuildingBuffer];
	s_pxGifPtr = g_pxGifBuffers[s_uCurrentGifBuffer];

	// and the end of buffer pointers - leaving space to add the end tags
	s_pxVifEnd = s_pxVifPtr + (ASURA_PS2_DEFERRED_RENDERING__VIF_BUFFER_SIZE>>4) - 1;
	s_pxGifEnd = s_pxGifPtr + (ASURA_PS2_DEFERRED_RENDERING__GIF_BUFFER_SIZE>>4) - 1;

	// Add the initial path3 open/close
	AddPath3Gate();
}

void Asura_PS2_DeferredRendering::ReturnVifBuffer( Asura_PS2_QW* pxBfr, u_int uQWs )
{
    Asura_Assert( pxBfr + uQWs == s_pxVifPtr, "" );
    s_pxVifPtr = pxBfr;
}

Asura_PS2_QW* Asura_PS2_DeferredRendering::GetVifBuffer( u_int uQWordCount )
{
    Asura_Assert( uQWordCount <= (ASURA_PS2_DEFERRED_RENDERING__VIF_BUFFER_SIZE >> 4), "" );

#if _DEBUG
    g_uLastVIFBfrQWCReturned = uQWordCount;
#endif

    // Will it fit in current buffer?
	if( s_pxVifPtr + uQWordCount < s_pxVifEnd )
	{
		Asura_PS2_QW* pBuf = s_pxVifPtr;
		s_pxVifPtr += uQWordCount;
		return pBuf;
	}

	// This buffer is full; end it and setup the next buffer.

	// Add an end tag to the VIF1 chain
    (reinterpret_cast<Asura_PS2_DmaTag*>(s_pxVifPtr))->SetWithNOPs( 0, Asura_PS2_DmaTag::PCE_NOCHANGE, Asura_PS2_DmaTag::ID_END, false, 0, false );

	// Make sure we havne't filled all the buffer and are about to overwrite the first one we created
	if (sv_uBuildingBuffer == sv_uLastBufferToDraw)
	{
		// We've filled all the buffers in one frame; there just isn't enough
        // buffer space for this frame.
		Asura_Logfile_Retail(( DEFERREDLOGFILE, OUT_OF_VIF_BFR_MSG ));
		Asura_Assert( false, OUT_OF_VIF_BFR_MSG );
        
        // This is a serious enough error that we should force a halt in all
        // builds. It's unlikely things could continue anyway. Even if they
        // did, the display would probably be so corrupted that the user
        // couldn't play on.
		asm __volatile__( "break;" );
	}

	// Move on to the next buffer
	sv_uBuildingBuffer++;
	sv_uBuildingBuffer &= VIF_BUFFER_QTY_MASK;

	// Check it's not being drawn at the moment
	if (sv_uBuildingBuffer == sv_uBufferDrawing)
	{
		// It is being drawn.. we need to wait for it to finish so we can move on
		// Switch off the interupt handler temporarily so we can catch the end of this buffers rendering in a bc0f loop
		( QueryIntrContext() ? iDisableDmac(DMAC_VIF1) : DisableDmac(DMAC_VIF1) );
		// Check the interrupt didn't get serviced whilst we tried to switch it of
		if (sv_uBuildingBuffer != sv_uBufferDrawing)
		{
			// Oh it did get service, the buffer is now free to be used - switch the interrupt handler back on
			( QueryIntrContext() ? iEnableDmac(DMAC_VIF1) : EnableDmac(DMAC_VIF1) );
		}
		else
		{
#ifndef ASURA_PS2_SONY_SUBMISSION
            {
                // Don't complain too often.
                static u_int uLastComplaint = 0;
                if( Asura_Timers::GetFrameNumber() - uLastComplaint >= 16 )
                {
                    Asura_OutputToDebugger_Retail(( VIF_BFR_STALL_MSG ));
                    uLastComplaint = Asura_Timers::GetFrameNumber();
                }
            }
#endif

			// Switch on the CPCond on channel 1
			DPUT_D_PCR(D_PCR_CPC1_M);
			// Make sure that's all written
			asm __volatile__("sync.l; sync.p");
			// Waits for the buffer to finish rendering
			asm __volatile__("VIFDMA_WaitForCurrentBufferToFinish:");
			asm __volatile__("bc0f VIFDMA_WaitForCurrentBufferToFinish");
			asm __volatile__("nop");

			// The buffer has finished, switch the interrupt handler back on - this will cause an interrupt immediately and do the buffer swapping as normal
            // (MP - 21.04.2004) Don't switch it back on if we've just waited
            // for the last buffer.
            if( sv_uBufferDrawing != sv_uLastBufferToDraw )
            {
                ( QueryIntrContext() ? iEnableDmac(DMAC_VIF1) : EnableDmac(DMAC_VIF1) );
            }
		}
	}

	// Setup the vif pointers
	s_pxVifPtr = g_pxVifBuffers[sv_uBuildingBuffer];
	
	// and the end of buffer pointers - leaving space to add the end tags
	s_pxVifEnd = s_pxVifPtr + (ASURA_PS2_DEFERRED_RENDERING__VIF_BUFFER_SIZE>>4) - 1;

	// Now there should be room
	Asura_PS2_QW *pBuf = s_pxVifPtr;
	s_pxVifPtr += uQWordCount;
	return pBuf;
}

Asura_PS2_QW* Asura_PS2_DeferredRendering::GetGifBuffer( u_int uQWordCount )
{ 
	Asura_PS2_QW *pBuf = s_pxGifPtr;
	s_pxGifPtr += uQWordCount;

	// Check we aren't about to over run the gif buffer
	if ( s_pxGifPtr >= s_pxGifEnd )
	{
		// GIF Buffer overrun !!!!
        
        const int iBytesUsed = ((s_pxGifPtr - g_pxGifBuffers[s_uCurrentGifBuffer]) << 4);

		Asura_Logfile_Retail(( DEFERREDLOGFILE, "GIF Buffer overrun !!!" ));
		Asura_Logfile_Retail(( DEFERREDLOGFILE, "\tGIF Buffer size = %i bytes" ,ASURA_PS2_DEFERRED_RENDERING__GIF_BUFFER_SIZE ));
		Asura_Logfile_Retail(( DEFERREDLOGFILE, "\tused = %i bytes" , iBytesUsed ));

		Asura_Assertf( false, ("GIF Buffer overrun - crash likely. (Used %i bytes from %i available.)", iBytesUsed, ASURA_PS2_DEFERRED_RENDERING__GIF_BUFFER_SIZE) );
	}

	return pBuf;
}

void Asura_PS2_DeferredRendering::AddPath3Gate()
{
	// Add an open/close path3 to the draw list
	Asura_PS2_QW *pxVifBuffer = GetVifBuffer(1);

	// Keep track of where we add this, so we cancle it later if the next texture turns out to be path 2 texture
	s_pxLastPath3Gate = pxVifBuffer;

	pxVifBuffer->u32s[0] = PY_DMA_CALL | 0;
	pxVifBuffer->u32s[1] = reinterpret_cast<u_int>(s_pxPath3Gate);
	pxVifBuffer->u32s[2] = 0;
	pxVifBuffer->u32s[3] = 0;

    if( dbg_bDeferredRenderingNeverOpenPATH3 )
    {
        pxVifBuffer->u32s[0] = PY_DMA_CNT | 0;
    }
}

void Asura_PS2_DeferredRendering::CancelLastPath3Gate( void )
{
	if( s_pxLastPath3Gate )
	{
	    // Replace the path 3 open/close CALL with an empty CNT.
        s_pxLastPath3Gate->u32s[0] = (Asura_PS2_DmaTag::ID_CNT << Asura_PS2_DmaTag::SHIFT_ID) | (0 << Asura_PS2_DmaTag::SHIFT_QWC);

		s_pxLastPath3Gate = NULL;
	}
}

void Asura_PS2_DeferredRendering::SendDeferredRenderingBlock( bool bPATH1Only )
{
	// Keep track of the first and last buffer we need to draw this time
	sv_uBufferDrawing  = (sv_uLastBufferToDraw+1) & VIF_BUFFER_QTY_MASK;
	sv_uLastBufferToDraw = sv_uBuildingBuffer;

#if _DEBUG
    if( dbg_bDeferredRenderingStopUpdatingRenderList )
    {
        if( g_bGotBfrsToRedraw )
        {
            sv_uBufferDrawing    = g_uFirstVifBfrToRedraw;
            sv_uLastBufferToDraw = g_uLastVifBfrToRedraw;
            s_uCurrentGifBuffer  = g_uGifBfrToRedraw;
        }
        else
        {
            g_uFirstVifBfrToRedraw = sv_uBufferDrawing;
            g_uLastVifBfrToRedraw  = sv_uLastBufferToDraw;
            g_uGifBfrToRedraw      = s_uCurrentGifBuffer;
            g_bGotBfrsToRedraw     = true;
        }
    }
    else
    {
        g_bGotBfrsToRedraw = false;
    }
#endif

	// Cancel the last past 3 open/close
	Asura_PS2_DeferredRendering::CancelLastPath3Gate();

	// Add an end tag to the VIF1 chain
	s_pxVifPtr->u32s[0] = PY_DMA_END | 0;		// DMA::End
	s_pxVifPtr->u32s[1] = 0;
	s_pxVifPtr->u32s[2] = SCE_VIF1_SET_MSKPATH3( 0, 0 );	// Unmask path 3 at the end
	s_pxVifPtr->u32s[3] = SCE_VIF1_SET_NOP( false );

	// Add an end tag to the GIF chain
	s_pxGifPtr->u32s[0] = PY_DMA_END | 0;		// DMA-End no data
	s_pxGifPtr->u32s[1] = 0;
//	s_pxGifPtr->u32s[2] = 0;					// Tags no transferred...
//	s_pxGifPtr->u32s[3] = 0;					// Tags no transferred...

#ifdef ASURA_PS2_DEFERRED_RENDERING__TRACK_BUFFER_USAGE
    // Update highest usage stats.
    {
	    // How much has our GIF list grown?
	    u_int uSpaceUsed = reinterpret_cast<u_int>(s_pxGifPtr) - reinterpret_cast<u_int>(g_pxGifBuffers[s_uCurrentGifBuffer]);
        g_uMaxGIFBufferUsage = Asura_Maths::Max( g_uMaxGIFBufferUsage, uSpaceUsed );

        // How much VIF1 buffer space have we used this single frame?
        u_int uFirst = sv_uBufferDrawing;
        u_int uLast = sv_uLastBufferToDraw;
        if( uFirst <= uLast )
        {
            uSpaceUsed = (s_pxVifPtr - g_pxVifBuffers[uFirst]) << 4;
        }
        else
        {
            // Wrapped round.
            uSpaceUsed = ((s_pxVifPtr - g_pxVifBuffers[0]) << 4) + (VIF_BUFFER_QTY - uFirst) * ASURA_PS2_DEFERRED_RENDERING__VIF_BUFFER_SIZE;
        }
        g_uMaxVIF1BufferUsage = Asura_Maths::Max( g_uMaxVIF1BufferUsage, uSpaceUsed );
    }
#endif

    if( bPATH1Only )
    {
        // Better not have any stuff waiting in the PATH3 buffer then!
        Asura_Assert( s_pxGifPtr == g_pxGifBuffers[s_uCurrentGifBuffer], "" );
    }
    else
    {
	    // Enable Mask Path 3 - ie stop path 3 being serviced until we are ready
	    MaskPath3( true );

	    // Start the Gif DMA - It'll pause until MskPath3 is opened
	    DPUT_D2_QWC( 0 );
	    DPUT_D2_TADR( reinterpret_cast<u_int>(Asura_PS2_CachedMem( g_pxGifBuffers[s_uCurrentGifBuffer]) ) );
	    DPUT_D2_CHCR( 1 |			// from memory
				     (1 << 2) |		// chain mode
				     (0 << 6) |		// transfer tag off
				     (1 << 8) );	// dma start

	    // Make sure that's all written
	    asm __volatile__("sync.l; sync.p");
    }

	// Now start the VIF1 DMA
	DPUT_D1_QWC( 0 );
DMA_CH1_GUARDED
	DPUT_D1_TADR( reinterpret_cast<u_int>(Asura_PS2_CachedMem( g_pxVifBuffers[sv_uBufferDrawing]) ) );

	// Clear the interrupt flag
	DPUT_D_STAT(D_STAT_CIS1_M);

	// If we have more than 1 buffer to send, switch on interrupt, else we'll leave them off so the bc0f loop works
	if ( sv_uLastBufferToDraw != sv_uBufferDrawing )
	{
		( QueryIntrContext() ? iEnableDmac(DMAC_VIF1) : EnableDmac(DMAC_VIF1) );
	}

	// Make sure that's all written
	asm __volatile__("sync.l; sync.p");
		
	// Off it goes ...
	DPUT_D1_CHCR( 1 |			// from memory
				 (1 << 2) |		// chain mode
				 (1 << 6) |		// transfer tag on
				 (1 << 7) |		// Enable interrupt flag
				 (1 << 8) );	// dma start

	// Make sure that's written
	asm __volatile__("sync.l; sync.p");

	// Swap buffers ready for the next block
	s_uCurrentGifBuffer++;
	s_uCurrentGifBuffer &= GIF_BUFFER_QTY_MASK;

	sv_uBuildingBuffer++;
	sv_uBuildingBuffer &= VIF_BUFFER_QTY_MASK;

	// Reset the chains vif and gif pointers
	s_pxVifPtr = g_pxVifBuffers[sv_uBuildingBuffer];
	s_pxGifPtr = g_pxGifBuffers[s_uCurrentGifBuffer];

	// and the end of buffer pointers - leaving space to add the end tags
	s_pxVifEnd = s_pxVifPtr + (ASURA_PS2_DEFERRED_RENDERING__VIF_BUFFER_SIZE>>4) - 1;
	s_pxGifEnd = s_pxGifPtr + (ASURA_PS2_DEFERRED_RENDERING__GIF_BUFFER_SIZE>>4) - 1;

	// Add the initial path3 open/close
	AddPath3Gate();
}

void Asura_PS2_DeferredRendering::WaitForDeferredRendering()
{
	// Switch off DMAC channel 1 interrupts - we'll take over sending the drawlist buffers from here
	( QueryIntrContext() ? iDisableDmac(DMAC_VIF1) : DisableDmac(DMAC_VIF1) );

	// Keep looping till all the buffers have been drawn
	while (1)
	{
		// Check their is some dma active
		if ( (DGET_D1_CHCR() & D_CHCR_STR_M) != 0)
		{
			// Vif DMA running

			// Clear the channel 1 finished flag
			DPUT_D_STAT(D_STAT_CIS1_M);
			// Switch on the CPCond on channel 1
			DPUT_D_PCR(D_PCR_CPC1_M);
			// Make sure that's all written
			asm __volatile__("sync.l; sync.p");
	
			// Check the Dn_CHCR.STR bit to ensure that you didn't accidentally clear the D_STAT.CISn bit just as the DMA finished
			if ( (DGET_D1_CHCR() & D_CHCR_STR_M) != 0)
			{
				// VIF dma still running

				// Waits for the deferred rendering to complete
				asm __volatile__("VIFDMA_WaitForVIFDeferredRendering:");
				asm __volatile__("bc0f VIFDMA_WaitForVIFDeferredRendering");
				asm __volatile__("nop");
			}
		}

		// Clear the channel 1 finished flag
		DPUT_D_STAT(D_STAT_CIS1_M);

		// DMA to VIF 1 has stopped - Have we drawn all the buffers
		if ( sv_uBufferDrawing == sv_uLastBufferToDraw )
		{
			// Yup the last buffer just finished drawing
			break;
		}

		// Nope, there are more to draw - send the next buffer
		sv_uBufferDrawing++;
		sv_uBufferDrawing &= VIF_BUFFER_QTY_MASK;

		// Now start the next block dmaing
DMA_CH1_UNGUARDED
		DPUT_D1_QWC( 0 );
DMA_CH1_GUARDED
		DPUT_D1_TADR( reinterpret_cast<u_int>(Asura_PS2_CachedMem( g_pxVifBuffers[sv_uBufferDrawing]) ) );

		// Make sure that's all written
		asm __volatile__("sync.l; sync.p");
		// Off it goes ...
		DPUT_D1_CHCR( 1 |			// from memory
					(1 << 2) |		// chain mode
					(1 << 6) |		// transfer tag on
					(1 << 7) |		// Enable interrupt flag
					(1 << 8) );	// dma start

		// Make sure that's started before we continue
		asm __volatile__("sync.l; sync.p");
	} 

DMA_CH1_UNGUARDED

	// Wait for the path3 transfers to complete.. they should have finished by now
	if ( (DGET_D2_CHCR() & D_CHCR_STR_M) == 0)
	{
		// No dma running
		return;
	}

	// Clear the channel 2 finished flag
	DPUT_D_STAT(D_STAT_CIS2_M);
	// Switch on the CPCond on channel 2
	DPUT_D_PCR(D_PCR_CPC2_M);
	// Make sure that's all written
	asm __volatile__("sync.l; sync.p");
	
	// Check the Dn_CHCR.STR bit to ensure that you didn't accidentally clear the D_STAT.CISn bit just as the DMA finished
	if ( (DGET_D2_CHCR() & D_CHCR_STR_M) == 0)
	{
		// No dma running
		return;
	}
	
	// Waits for the deferred rendering to complete
	asm __volatile__("VIFDMA_WaitForGIFDeferredRendering:");
	asm __volatile__("bc0f VIFDMA_WaitForGIFDeferredRendering");
	asm __volatile__("nop");
}

//	Adds a DDA-Check mark for the performance analyser
//void Asura_PS2_DeferredRendering::AddPAMark()
//{
//	Asura_PS2_QW* pxOut = GetVifBuffer(3);
//	pxOut->u32s[0] = PY_DMA_CNT | 2;
//	pxOut->u32s[1] = 0;
//	pxOut->u32s[2] = SCE_VIF1_SET_NOP(false);
//	pxOut->u32s[3] = SCE_VIF1_SET_DIRECT(2, false);
//	pxOut++;
//
//	pxOut->u64s[0] = SCE_GIF_SET_TAG( 1, 1, 0, 0, SCE_GIF_PACKED, 1);
//	pxOut->u64s[1] = SCE_GIF_PACKED_AD;
//	pxOut++;
//
//	pxOut->u32s[0] = 0;
//	pxOut->u32s[1] = 0;
//	pxOut->u32s[2] = 0x63;		/// Reserved PA Marker register
//	pxOut->u32s[3] = 0;
//}

// P R I V A T E   C L A S S   M E T H O D S /////////////////////////////////////////
void Asura_PS2_DeferredRendering::MaskPath3( const bool bMask )
{
	// Update the maskpath3 dma block
	s_pxMaskPath3->u32s[2] = SCE_VIF1_SET_MSKPATH3( ((bMask) ? 0x8000 : 0x0000), 0 );

	// Fire it off
	DPUT_D1_QWC(0);
	DPUT_D1_TADR((u_int)Asura_PS2_CachedMem(s_pxMaskPath3));

	// Clear the channel 1 finished flag
	DPUT_D_STAT(D_STAT_CIS1_M);
	// Switch on the CPCond on channel 1
	DPUT_D_PCR(D_PCR_CPC1_M);
	// Make sure that's all written
	asm __volatile__("sync.l; sync.p");

	// start actual kick
	DPUT_D1_CHCR( (1<<0) |	// from memory
				  (1<<2) |	// chain mode
				  (1<<6) |	// transfer tag on
				  (1<<8));	// dma start

	// Make sure it's started
	asm __volatile__("sync.l; sync.p");

	// Wait for it to finsh
	asm __volatile__("DMA_MaskPath3:");
	asm __volatile__("bc0f DMA_MaskPath3");
	asm __volatile__("nop");
}

int Asura_PS2_DeferredRendering::BufferSwapInteruptHandler( int channel )
{
	// Check if the last buffer just finished drawing
	if (channel == DMAC_VIF1) 
	{
		if ( sv_uBufferDrawing != sv_uLastBufferToDraw )
		{
			// Nope, there are more to draw - send the next buffer
			sv_uBufferDrawing++;
			sv_uBufferDrawing &= VIF_BUFFER_QTY_MASK;

			// Now start the next block dmaing
DMA_CH1_UNGUARDED
			DPUT_D1_QWC( 0 );
DMA_CH1_GUARDED
			DPUT_D1_TADR( reinterpret_cast<u_int>(Asura_PS2_CachedMem( g_pxVifBuffers[sv_uBufferDrawing]) ) );
			// Clear the interrupt flag - this is done for us...
			DPUT_D_STAT(D_STAT_CIS1_M);
			// If this is the last buffer, switch off the intterup flag so that the wait loop can sit in a bc0f loop
			if ( sv_uBufferDrawing == sv_uLastBufferToDraw )
			{
				// Switch off DMAC channel 1 interrupts
				iDisableDmac(DMAC_VIF1);
			}

			// Make sure that's all written
			asm __volatile__("sync.l; sync.p");
			// Off it goes ...
			DPUT_D1_CHCR( 1 |			// from memory
						(1 << 2) |		// chain mode
						(1 << 6) |		// transfer tag on
						(1 << 7) |		// Enable interrupt flag
						(1 << 8) );	// dma start

			// Make sure that's started before we continue
			asm __volatile__("sync.l; sync.p");
		}
		else
		{
			// We seemed to have finished drawing.. so we should never get here
			asm("break 1");

			// Switch off DMAC channel 1 interrupts and pray I guess
			iDisableDmac(DMAC_VIF1);
		}
	}

	// We're not greedy, let the other handlers have thier say too
	asm volatile("sync.l; ei");
	return 1;
}
#endif
