//
// Asura_PSP_Graphics.cpp
//
// Created by KJFL - Friday, February 06, 2004
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "Rendering/Asura_PSP_Graphics.h"

// PSP includes
#include <ctrlsvc.h>	//needed for controller functions
#include <displaysvc.h>	//needed for VSync
#include <kernel.h>
#include <psptypes.h>
#include <iofilemgr.h>
#include <moduleexport.h>
#include <psperror.h>
#include <geman.h> // needed for sceGeEdramGetAddr
#if defined(ASURA_PSP_ENABLE_SCREENSHOTS)
#if defined(ASURA_PSP_UMD) || defined(ASURA_PSP_ARTIST_BUILD)
#include <fatms.h> // needed for checking memory stick presence
#endif // defined(ASURA_PSP_ARTIST_BUILD)
#include <rtcsvc.h> // needed for clock
#endif	// defined(ASURA_PSP_ENABLE_SCREENSHOTS)

// Asura headers
#include "Camera/Asura_Camera.h"
#include "Core/Asura_MaterialFlags.h"
// for debug text
#include "Core/Asura_PlatformBasics.h"
#include "Core/Asura_PSP_VolatileMemoryManagement.h"
#include "Core/Asura_String.h"
#include "Core/Asura_Timers.h"
#include "Core/Memory/Asura_Mem.h"
#include "Debugging/Asura_Logfile.h"
#include "Debugging/Asura_PSP_GPIO.h"
#if defined(ASURA_USE_SCREENSHOT_SYSTEM)
#include "Debugging/Asura_ScreenShot_System.h"
#endif	// defined(ASURA_USE_SCREENSHOT_SYSTEM)
#ifdef ASURA_DEBUG_VARIABLE_SYSTEM
#include "Interface/Asura_CommandConsole.h"
#endif // #ifdef ASURA_DEBUG_VARIABLE_SYSTEM
#include "Light/Asura_GI_Solution.h"
#include "Rendering/Asura_Colour.h"
#include "Rendering/Asura_Fog.h"
#include "Rendering/Asura_PSP_Fog.h"
#include "Rendering/Asura_PSP_MotionBlur.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Viewport.h"

#ifdef ASURA_PSP_PROFILING
#include "Debugging/Asura_CPUProfile.h"
#include "Debugging/Asura_PSP_Profiling.h"
#endif //#ifdef ASURA_PSP_PROFILING

#include "Rendering/Asura_PSP_Filter.h"
#include "Rendering/Asura_PSP_RenderStates.h"
#include "Rendering/Asura_PSP_VRAMManagement.h"
#include "Rendering/Asura_PSP_Stencil_Enums.h"
#include "Rendering/Asura_PSP_TextureManagement.h"
#include "Rendering\Asura_PSP_StencilBufferManager.h"

#if !defined(ASURA_PSP_DISPLAY_LIST_SIZE)
// KOR - 21/11/2005 - brought display list size back down from 1Mb(!) to 64Kb.
// It was originally taken up to 1Mb when we moved from emulator to hardware;
// the emulator didn't actually use it, if I remember correctly, and we had
// a very high number of draw calls due to an inefficient particle renderer.
// Now that's been fixed, hopefully we can get away with 64Kb.
#define ASURA_PSP_DISPLAY_LIST_SIZE (ASURA_PSP_DEFAULT_DISPLAY_LIST_SIZE)
#endif	// !defined(ASURA_PSP_DISPLAY_LIST_SIZE)

#ifndef ASURA_PSP_DEFERREDRENDER
	#define ASURA_PSP_SYNC_ON_BEGINFRAME (1)
#endif

#ifdef ASURA_PSP_DEFERREDRENDER
	static u_char				g_auDisplayList[ 2 ][ ASURA_PSP_DISPLAY_LIST_SIZE ] __attribute__( ( aligned( 64 ) ) );
#else
	static u_char				g_auDisplayList[ 1 ][ ASURA_PSP_DISPLAY_LIST_SIZE ] __attribute__( ( aligned( 64 ) ) );
#endif

static Asura_PSP_Matrix4	g_axMatrixStack[8+8+32+2];

// Static members
// Public
bool								Asura_PSP_Graphics::s_bRecordImages = false;
u_int								Asura_PSP_Graphics::s_uCurrentImage = 0;
// Private
bool								Asura_PSP_Graphics::s_bInitialised = false;
bool								Asura_PSP_Graphics::s_bUseColourDoubling = false;
bool								Asura_PSP_Graphics::s_bUseHighContrastEntityLighting = false;
bool								Asura_PSP_Graphics::s_bUseLighting = false;
bool								Asura_PSP_Graphics::s_bVsyncOn = true;
bool								Asura_PSP_Graphics::s_bTransparentEntityWriteToZBuffer = true; 
u_int								Asura_PSP_Graphics::s_uNumLights = 0;
ASURA_PSP_GRAPHICS_MATERIALTYPE		Asura_PSP_Graphics::s_eMaterialType = ASURA_PSP_GRAPHICS_MATERIALTYPE_NONE;

u_int								Asura_PSP_Graphics::s_uCurrentRenderingBuffer;
u_int								Asura_PSP_Graphics::s_uCurrentDisplayedBuffer;
#ifndef ASURA_PSP_SONY_SUBMISSION
bool								Asura_PSP_Graphics::s_bInFrameForAssert;
u_int								Asura_PSP_Graphics::s_uMaxDisplayList = 0;
#endif //#ifndef ASURA_PSP_SONY_SUBMISSION
u_int								Asura_PSP_Graphics::s_uCurrentDisplayList = 0;
bool								Asura_PSP_Graphics::s_bDeferredImmediateDraw = false;

Asura_PSP_Graphics::fnStateOverride *Asura_PSP_Graphics::s_pfnStateOverride = NULL;

// KOR - 07/03/2006 - this will scale the screen, so that
// what would normally be the guardband area fills the screen,
// and the screen becomes a small area in the center. Useful
// for debugging software guardband clipping.
DEBUGVAR bool dbg_bShowGuardbandArea = false;
DEBUGVAR float dbg_fShowGuardbandAreaZoomIn = 1.0f; 

// ART - 05/07/2006 - I have no idea why Kim added the first of
// these perspective hacks, but to work correct the code also needs
// the second one. Basically, Kim's hack tends the far clip to infinity,
// for the perspective matrix - but there are actually 2 elements in
// the matrix which reference the far-clip and so they both need to be
// "hacked". The WZ hack value should actually be -2.0 but this has an
// adverse affect on the near-clipping plane so we've left somewhere
// between the 2 entries. There is a further description in the 
// "SetupPSPProjectionMatrix" function where these values are used.

// AJG - 24/01/07 - I think the WZ value actually tends to -2 * near plane value, updating the hack to reflect this
DEBUGVAR float dbg_fPerspectiveMatrixZZHack = -1.0f;
//DEBUGVAR float dbg_fPerspectiveMatrixWZHack = -0.75f;
DEBUGVAR bool dbg_bUsePerspectiveMatrixWZHack = true;

#ifdef ASURA_PSP_ENABLE_SCREENSHOTS
// Manual screenshot flag...
bool							Asura_PSP_Graphics::s_bTakeManualScreenshot	= false;

// For retrieving the environment filename
extern char* DefaultFileToLoad;

typedef struct
{
    unsigned char IDfieldLength;        // = 0
    unsigned char ColourMapType;        // = 0
    unsigned char ImageType;            // = 0
    unsigned char pad;
    unsigned short firstColourMapEntry; // = 0
    unsigned short colourMapLength;     // = 0
    unsigned short xOrigin;             // = 0
    unsigned short yOrigin;             // = 0
    unsigned short imageWidth;
    unsigned short imageHeight;
    unsigned char bitsPerPixel;         // 16, 24 or 32
    unsigned char imageDescriptor;      // = 0x20
} TargaHeader;

typedef struct
{
    unsigned int extensionAddr;         // = 0
    unsigned int developerAddr;         // = 0
    char    IDstring[18];               // = "TRUEVISION-TARGA."
    //char    terminator;                 // = 0
} TargaFooter;

static u_char*	g_pucVolatileMemory	= NULL;

static void AllocateVolatileMemory (u_char* pucDataStart)
{
	g_pucVolatileMemory	= pucDataStart;
}

static void DeallocateVolatileMemory (u_char* pucDataStart)
{
	g_pucVolatileMemory	= NULL;
}

void Asura_PSP_Graphics::SaveTarga(char* szFilename, int iWidth, int iHeight, int iBitsPerPixel, void *pData)
{
	const u_int uScreenBufferSize = iWidth * iHeight * (iBitsPerPixel >> 3);
	const u_int uHeaderSize = 18;
	const u_int uFooterSize = 26;
	const u_int uFileSize = uScreenBufferSize + uHeaderSize + uFooterSize;
	TargaHeader xHeader = { 0, 0, 2, // uncompressed
							0, 0, 0,
							0, 0, 0, 0, 32, 0x20 }; // 32bit, top,left->right,bottom
	TargaFooter xFooter = { 0, 0, "TRUEVISION-TARGA."};//, 0 };

	xHeader.bitsPerPixel = iBitsPerPixel;
	xHeader.imageWidth  = iWidth;
	xHeader.imageHeight = iHeight;

	Asura_Handle uVolatileMemoryHandle = Asura_PSP_VolatileMemoryManagement::Allocate(uFileSize,AllocateVolatileMemory,DeallocateVolatileMemory);

	if(uVolatileMemoryHandle == ASURA_INVALID_HANDLE) { return; }

	char* pcBufferStart = (char*)g_pucVolatileMemory;
	char* pcBuffer = pcBufferStart;

	// Copy the header into the buffer.
	Asura_Mem::Copy(pcBuffer, &xHeader, uHeaderSize);
	pcBuffer += uHeaderSize;

	// Store the pointer to the start of the screenbuffer so we can play with it later.
	u_short* const pusScreenShotBuffer = (u_short*)pcBuffer;
	// Copy VRAM into the buffer.
	Asura_Mem::Copy(pcBuffer, pData, uScreenBufferSize);
	pcBuffer += uScreenBufferSize;

	// Copy the footer into the buffer
	Asura_Mem::Copy(pcBuffer, &xFooter, uFooterSize);
	pcBuffer += uFooterSize;

	// Swap the channels
	if (iBitsPerPixel == 32) // 32 bit
	{
		for (int iPixel=0; iPixel < (iWidth * iHeight); iPixel++)
		{
			// Have to deal with the channels as shorts, because the screenshot buffer is not word aligned.
			const u_short	usGreen	= (pusScreenShotBuffer[iPixel * 2 + 0] & 0xFF00) >> 8;
			const u_short	usBlue	= (pusScreenShotBuffer[iPixel * 2 + 0] & 0x00FF);
			const u_short	usAlpha	= 0x00FF;	//(pusScreenShotBuffer[iPixel * 2 + 1] & 0xFF00) >> 8;
			const u_short	usRed	= (pusScreenShotBuffer[iPixel * 2 + 1] & 0x00FF);
			pusScreenShotBuffer[iPixel * 2 + 0] = (usGreen << 8) | usRed;
			pusScreenShotBuffer[iPixel * 2 + 1] = (usAlpha << 8) | usBlue;
		}
	}
	else // 16 bit
	{
		unsigned short usTemp, usConvert;

		xHeader.imageHeight *= 2;
		for (int iPixel=0; iPixel<(iWidth * iHeight); iPixel++)
		{
			usTemp = pusScreenShotBuffer[iPixel];

			usConvert =((unsigned int)((usTemp>>0) &0x1f))<<10;
			usConvert+=((unsigned int)((usTemp>>5) &0x1f))<<5;
			usConvert+=((unsigned int)((usTemp>>10)&0x1f))<<0;

			pusScreenShotBuffer[iPixel] = usConvert;
		}
	}

	sceKernelDcacheWritebackAll();

	// Try to open the file.
	SceUID iFileID = sceIoOpen (szFilename, SCE_O_CREAT | SCE_O_WRONLY, 0);
	Asura_Assert(iFileID >= 0, "Failed to open screenshot file for writing");
	if (iFileID >= 0)
	{
		// Write whole file.
		sceIoWrite (iFileID, pcBufferStart, uFileSize);

		// Close file
		sceIoClose(iFileID);
	}

	// Unlock the volatile memory so that the utility can use it again.
	Asura_PSP_VolatileMemoryManagement::Deallocate(uVolatileMemoryHandle);
}

void Asura_PSP_Graphics::SaveCurrentViewAsTarga(void)
{
#if defined(ASURA_PSP_UMD) || defined(ASURA_PSP_ARTIST_BUILD)
	const bool bCanWrite = (sceFatmsIsAssigned() == 1);
	const char szPrefix[5] = "ms0";
#else
	const bool bCanWrite = true;
	const char szPrefix[6] = "host0";
#endif
	if(bCanWrite)
	{
		static char acBuffer[64];

		// First ensure we have a directory to put the shot in.
		Asura_PrintfToBfr(acBuffer, "%s:sshots", 64, true, szPrefix);
		sceIoMkdir(acBuffer, 0);

		ScePspDateTime xClock;
		sceRtcGetCurrentClockLocalTime(&xClock);	
		// Now construct the screenshot name.
		Asura_String xStrippedEnvName;
		if(DefaultFileToLoad && DefaultFileToLoad[0] != 0)
		{
			Asura_String xEnvName = DefaultFileToLoad;
			xEnvName.SplitFilename(0, &xStrippedEnvName, 0);
		}
		else
		{
			// Default to something sensible if we can't retrieve an environment name
			xStrippedEnvName = "pspscreen";
		}
		Asura_PrintfToBfr(acBuffer, "%s:sshots\\%s-%04d%02d%02d-%02d-%02d-%02d.tga", 64, true, szPrefix, xStrippedEnvName.GetString(),
			xClock.year, xClock.month, xClock.day, xClock.hour, xClock.minute, xClock.second);
		SaveTarga(acBuffer, 512, SCEGU_SCR_HEIGHT, 32, (void*)((char*)sceGeEdramGetAddr() + GetCurrentRenderingBuffer()));
	}
}
#endif//def ASURA_PSP_ENABLE_SCREENSHOTS

void Asura_PSP_Graphics::Initialise(void)
{
	if(s_bInitialised) return;
	s_bInitialised = true;

	Asura_Viewport::Set( SCEGU_SCR_WIDTH, SCEGU_SCR_HEIGHT );
    sceCtrlSetSamplingMode(SCE_CTRL_MODE_DIGITALANALOG);                // for analog input
    sceCtrlSetSamplingCycle(0);

	GU_Init();
	GU_Display(SCEGU_DISPLAY_ON);

	s_uCurrentRenderingBuffer = reinterpret_cast< uint32 >( SCEGU_VRAM_BP32_0 );
	s_uCurrentDisplayedBuffer = reinterpret_cast< uint32 >( SCEGU_VRAM_BP32_1 );

	// Initialise drawing buffers (none-context specific)
	GU_Start( SCEGU_IMMEDIATE, g_auDisplayList[ 0 ], ASURA_PSP_DISPLAY_LIST_SIZE );

	GU_DrawBuffer( SCEGU_PF8888, s_uCurrentRenderingBuffer, SCEGU_VRAM_WIDTH );
	GU_DispBuffer( SCEGU_SCR_WIDTH, SCEGU_SCR_HEIGHT, s_uCurrentDisplayedBuffer, SCEGU_VRAM_WIDTH );
	GU_DepthBuffer( SCEGU_VRAM_BP32_2, SCEGU_VRAM_WIDTH );

	// KOR - with some builds, we get a strange effect where you get two purple images at the top of the screen. Switching to this format
	// fixes it; however, this is crappy 16-bit colour.
	/*	GU_DrawBuffer(SCEGU_PF5650, SCEGU_VRAM_BP_0, SCEGU_VRAM_WIDTH);
	GU_DispBuffer(SCEGU_SCR_WIDTH, SCEGU_SCR_HEIGHT, SCEGU_VRAM_BP_1, SCEGU_VRAM_WIDTH);

	// KJFL - hmm, switching to 16bit *and then* 32bit seems to fix the problem too. But I won't swear to it.
	GU_DrawBuffer(SCEGU_PF8888, SCEGU_VRAM_BP32_0, SCEGU_VRAM_WIDTH);
	GU_DispBuffer(SCEGU_SCR_WIDTH, SCEGU_SCR_HEIGHT, SCEGU_VRAM_BP32_1, SCEGU_VRAM_WIDTH);*/

	GU_Finish();
	GU_Sync( SCEGU_SYNC_FINISH, SCEGU_SYNC_WAIT );

	// Context specific initialisation
#ifdef ASURA_PSP_DEFERREDRENDER
	GU_Start( SCEGU_LIST, g_auDisplayList[ 0 ], ASURA_PSP_DISPLAY_LIST_SIZE );
#else
	GU_Start( SCEGU_IMMEDIATE, g_auDisplayList[ 0 ], ASURA_PSP_DISPLAY_LIST_SIZE );
#endif

	GU_Offset(SCEGU_SCR_OFFSETX, SCEGU_SCR_OFFSETY);
	GU_Viewport(2048, 2048, SCEGU_SCR_WIDTH, SCEGU_SCR_HEIGHT);
	GU_DepthRange(0, 65535);

	//	Asura_PSP_Vector3	vec;
	//GU_BlendFunc(SCEGU_ADD, SCEGU_SRC_ALPHA, SCEGU_ONE_MINUS_SRC_ALPHA, 0, 0);
	//GU_Enable(SCEGU_BLEND);
	GU_Disable(SCEGU_BLEND);

	GU_Scissor(0, 0, SCEGU_SCR_WIDTH, SCEGU_SCR_HEIGHT);
	GU_Enable(SCEGU_SCISSOR_TEST);

	GU_AlphaFunc(SCEGU_GREATER, 0x00, 0xff);
	GU_Enable(SCEGU_ALPHA_TEST);

	GU_DepthFunc(SCEGU_LEQUAL);
	GU_Enable(SCEGU_DEPTH_TEST);

	GU_FrontFace(SCEGU_CCW);
	GU_Enable(SCEGU_CULL_FACE);

	GU_Enable(SCEGU_TEXTURE);
	GU_TexFunc(SCEGU_TEX_MODULATE, SCEGU_RGBA);
	GU_TexFilter(SCEGU_LINEAR_MIPMAP_LINEAR, SCEGU_LINEAR);
	GU_TexLevelMode(SCEGU_LOD_SLOPE, 0.f);
	Asura_PSP_RenderStates::RequestTexSlope(0.125f); // TODO - There are ways to calculate this on a per-frame basis to always get the optimal result. However, this value seems to produce reasonable results all the time, so we may be OK just keeping it like this and not wasting the cycles.
	GU_TexWrap(SCEGU_REPEAT, SCEGU_REPEAT);
	GU_TexMode(SCEGU_PFIDX8, 2, SCEGU_SINGLE_CLUT, SCEGU_TEXBUF_FAST);
	GU_Color(0xffffffff);
	//GU_Disable(SCEGU_TEXTURE);

	GU_ShadeModel(SCEGU_SMOOTH);

	GU_Enable(SCEGU_CLIP_PLANE);

	GU_Disable(SCEGU_LIGHTING);
	GU_Disable(SCEGU_LIGHT0);
	GU_Disable(SCEGU_LIGHT1);
	GU_Disable(SCEGU_LIGHT2);
	GU_Disable(SCEGU_LIGHT3);

	// KJFL - 28 February 2005 - even when disabled, directional lights are quicker than point lights!?
	ScePspFVector3* pxDummyVector = (ScePspFVector3*)(&Asura_Maths::ZeroVector3);
	GU_Light(0, SCEGU_LIGHT_DIRECTION, SCEGU_DIFFUSE, pxDummyVector);
	GU_Light(1, SCEGU_LIGHT_DIRECTION, SCEGU_DIFFUSE, pxDummyVector);
	GU_Light(2, SCEGU_LIGHT_DIRECTION, SCEGU_DIFFUSE, pxDummyVector);
	GU_Light(3, SCEGU_LIGHT_DIRECTION, SCEGU_DIFFUSE, pxDummyVector);

	GU_Disable(SCEGU_FOG);
	Asura_PSP_RenderStates::RequestAmbient(0xff000000);
	GU_Material(SCEGU_SPECULAR,0xffff0000);

	GUM_SetMatrixStack(&g_axMatrixStack[0], 8, 8, 32, 2);

	SetupPSPProjectionMatrix(SCEGU_RAD(45.0f), 0.0f);

	GUM_MatrixMode(SCEGU_MATRIX_WORLD);

	GU_ClearColor(0x00333333);
	GU_ClearDepth(65535);
	Asura_PSP_StencilBufferManager::SetStencilBufferClearBits(ASURA_PSP_STENCIL_NONE);

	uint32 uDisplayListSize = GU_Finish();

#ifdef ASURA_PSP_DEFERREDRENDER
	sceKernelDcacheWritebackRange( g_auDisplayList[ 0 ], uDisplayListSize );

	// Start this frame's display list drawing (GPU) whilst the next frame executes (CPU)
	GU_SendList( SCEGU_QUEUE_TAIL, g_auDisplayList[ 0 ], NULL, 0, NULL );
#endif

	GU_Sync( SCEGU_SYNC_FINISH, SCEGU_SYNC_WAIT );
//    sceDisplayWaitVblankStart();

#ifdef ASURA_DEBUG_VARIABLE_SYSTEM
	Asura_CommandConsole::AddVar( L"Debug.VSyncOn", &s_bVsyncOn, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif
}

void Asura_PSP_Graphics::Shutdown()
{
	s_bInitialised = false;
}

void Asura_PSP_Graphics::SetupPSPMatFromAsuraData( const Asura_Matrix_3x3& xOri, const Asura_Vector_3& xPos )
{
	GUM_MatrixMode(SCEGU_MATRIX_VIEW);

	Asura_PSP_Matrix4 xMatrix;

	xMatrix.x.x = xOri.e11;
	xMatrix.x.y = -xOri.e12;
	xMatrix.x.z = -xOri.e13;
	xMatrix.y.x = xOri.e21;
	xMatrix.y.y = -xOri.e22;
	xMatrix.y.z = -xOri.e23;
	xMatrix.z.x = xOri.e31;
	xMatrix.z.y = -xOri.e32;
	xMatrix.z.z = -xOri.e33;

	xMatrix.w.x = 0;//xPos.x;
	xMatrix.w.y = 0;//xPos.y;
	xMatrix.w.z = 0;//xPos.z;

	xMatrix.w.w = 1.0f;

	xMatrix.x.w = 0;
	xMatrix.y.w = 0;
	xMatrix.z.w = 0;

	GUM_LoadMatrix(&xMatrix);

	
	Asura_PSP_Vector3 xV = { -xPos.x, -xPos.y, -xPos.z };

	GUM_Translate(&xV);

	GUM_MatrixMode(SCEGU_MATRIX_WORLD);
	GUM_LoadIdentity();
}

static float g_fTexSlope = 0.3f;

void Asura_PSP_Graphics::BeginFrame(void)
{
	LockMutex();

	// Cache the GPI switch data...
	Asura_PSP_GPIO::CacheGPI();

#ifdef ASURA_PSP_SYNC_ON_BEGINFRAME
	static bool g_bFirstFrame = true;
	if (!g_bFirstFrame)
	{
		VSync();
	}
	else
	{
		g_bFirstFrame = false;
	}
#endif
	
#ifdef ASURA_PSP_PROFILING
	Asura_PSP_Profiling::InstallSignalHandler(); 
#endif // #ifdef ASURA_PSP_PROFILING

#ifdef ASURA_PSP_DEFERREDRENDER
	GU_Start( SCEGU_LIST, g_auDisplayList[ s_uCurrentDisplayList ], ASURA_PSP_DISPLAY_LIST_SIZE );
	GU_DrawBufferList( SCEGU_PF8888, s_uCurrentRenderingBuffer, SCEGU_VRAM_WIDTH );
#else
	GU_Start( SCEGU_IMMEDIATE, g_auDisplayList[ 0 ], ASURA_PSP_DISPLAY_LIST_SIZE );
#endif

	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);
	// KOR - 11/08/2004 - This clear doesn't actually work on the z-buffer unless z-buffer writes are enabled
	// (which vaguely makes sense, but had me cursing the thing for an hour while I figured it out...)

#ifdef ASURA_PSP_PROFILING
	Asura_PSP_Profiling::Signal( Asura_PSP_Profiling::ASURA_PSP_SIGNAL_IDLE_END ); 
	Asura_PSP_Profiling::Signal(Asura_PSP_Profiling::ASURA_PSP_SIGNAL_CLEAR_START); 
#endif // #ifdef ASURA_PSP_PROFILING

	// (AW - 9/1/06) - I'm a bit worried about this code. I found that when the above profiling line is removed
	// (like in a submission build with no profiling), then when playing FMVs this screen clear appears to end 
	// up on the wrong display list, making FMVs come out black. My fix for this bug has been to disable screen 
	// clears during FMVs, which is really coding around the problem rather than fixing. I worry this bug may 
	// rear its head in other circumstances.
	if(Asura_Fog::IsFogEnabled())
	{
		const Asura_RGBA&	xFogColour	= Asura_Fog::GetFogColour();
		const u_int			uFogColour	= xFogColour.ToUIntABGR();

		// Use the fog colour when clearing the screen...
		GU_ClearColor(uFogColour);
	}
	else
	{
		GU_ClearColor(0x00000000);
	}

	if (Asura_Render::GetEnableScreenClear())
	{
		GU_Clear(SCEGU_CLEAR_FASTALL);
	}
	else
	{
		GU_Clear(SCEGU_CLEAR_FAST | SCEGU_CLEAR_DEPTH | SCEGU_CLEAR_STENCIL);
	}

	
#ifdef ASURA_PSP_PROFILING
	Asura_PSP_Profiling::Signal(Asura_PSP_Profiling::ASURA_PSP_SIGNAL_CLEAR_END); 
#endif // #ifdef ASURA_PSP_PROFILING


	SetupPSPMatFromAsuraData( *Asura_Camera::GetOrientation(), *Asura_Camera::GetPosition());
	SetupPSPProjectionMatrix(0.0f);

	Asura_PSP_RenderStates::RequestTexSlope(g_fTexSlope);

	Asura_PSP_Fog::OnBeginFrame();
	
	Asura_PSP_VRAMManagement::OnBeginFrame(); 

}

extern void FlushQuads();
#ifdef ASURA_USE_ORIGINAL_PARTICLE_SYSTEM
extern void FlushParticles();
#endif
extern void FlushBeams();
extern void FlushEnv();

void Asura_PSP_Graphics::VSync()
{
/*
	char szBuffer[200];
	sprintf(szBuffer, "Number of tris rendered: %d", Asura_Render::NumberOfTrianglesRendered	);
	GU_DebugPrint(8,24, 0x000ff00, szBuffer);
	sprintf(szBuffer, "Number of strips rendered: %d", Asura_Render::NumberOfPatchesRendered	);
	GU_DebugPrint(8,32, 0x000ff00, szBuffer);*/

#ifdef ASURA_PSP_PROFILING
	ASURA_CPU_PROFILE__START_LABEL( ASURA_CPU_PROFILE__ENGINE_DARK_BLUE, "GPU synchronisation" );
#endif	// ASURA_PSP_PROFILING
	GU_Sync( SCEGU_SYNC_FINISH, SCEGU_SYNC_WAIT );
#ifdef ASURA_PSP_PROFILING
	ASURA_CPU_PROFILE__STOP(ASURA_CPU_PROFILE__ENGINE_DARK_BLUE);
#endif	// ASURA_PSP_PROFILING

#ifndef ASURA_PSP_SCEEGU
	if (s_bVsyncOn && !Asura_PSP_GPIO::GetGPI(ASURA_PSP_GPI_DISABLE_VSYNC))
	{
#ifdef ASURA_PSP_PROFILING
		ASURA_CPU_PROFILE__START_LABEL(ASURA_CPU_PROFILE__ENGINE_DARK_GREEN, "V-Sync");
#endif	// ASURA_PSP_PROFILING
		sceDisplayWaitVblankStartCB();
#ifdef ASURA_PSP_PROFILING
		ASURA_CPU_PROFILE__STOP(ASURA_CPU_PROFILE__ENGINE_DARK_GREEN);
#endif	// ASURA_PSP_PROFILING
	}
#endif

	SwapBuffers();

#ifdef ASURA_PSP_SCEEGU
	if (s_bVsyncOn && !Asura_PSP_GPIO::GetGPI(ASURA_PSP_GPI_DISABLE_VSYNC))
	{
#ifdef ASURA_PSP_PROFILING
		ASURA_CPU_PROFILE__START_LABEL(ASURA_CPU_PROFILE__ENGINE_DARK_GREEN, "V-Sync");
#endif	// ASURA_PSP_PROFILING
		sceDisplayWaitVblankStartCB();
#ifdef ASURA_PSP_PROFILING
		ASURA_CPU_PROFILE__STOP(ASURA_CPU_PROFILE__ENGINE_DARK_GREEN);
#endif	// ASURA_PSP_PROFILING
	}
#endif

	// KOR - 01/03/2006 - Cycle the GPO lights. Should help us tell if we're running or not.
	// Updates every 4 frames (doing it every frame is a bit mental)
	// Cycles the first 4 GPI lights to indicate normal operation. We'll use the last 4 for error codes.
	Asura_PSP_GPIO::SetGPO((1 << (Asura_Timers::GetFrameNumber() >> 2) % 4));
#ifdef ASURA_PSP_ENABLE_SCREENSHOTS
	if(Asura_PSP_GPIO::GetGPI(ASURA_PSP_GPI_SCREENSHOT))
	{
		// Take the screenshot...
		SaveCurrentViewAsTarga();

		// Pause on this screen until switch is turned off again
		do 
		{
			Asura_PSP_GPIO::CacheGPI();

		} while(Asura_PSP_GPIO::GetGPI(ASURA_PSP_GPI_SCREENSHOT));
	}
	else if(s_bTakeManualScreenshot)
	{
		// Take the screenshot...
		SaveCurrentViewAsTarga();

		// Reset the flag...
		s_bTakeManualScreenshot	= false;
	}
#endif//def ASURA_PSP_ENABLE_SCREENSHOTS

	FlushQuads();
#ifdef ASURA_USE_ORIGINAL_PARTICLE_SYSTEM
	FlushParticles();
#endif
	FlushBeams();
	FlushEnv();
}

// JG - 25/07/06: Swap buffers now keep track of the buffer the GU is using to draw.
// Assuming buffer swaps were succesful was quite dangerous, in some situations leading to draw / display buffers getting messed up 
// (out of sync) i.e. the FMV player rendering to the front buffer.
void Asura_PSP_Graphics::SwapBuffers(void)
{
	const u_int uTemp = (u_int) GU_SwapBuffers();
	s_uCurrentRenderingBuffer = uTemp;
	if (s_uCurrentRenderingBuffer == (u_int)SCEGU_VRAM_BP32_0)
	{
		s_uCurrentDisplayedBuffer = (u_int)SCEGU_VRAM_BP32_1;
	}
	else
	{
		s_uCurrentDisplayedBuffer = (u_int)SCEGU_VRAM_BP32_0;
	}

#ifdef ASURA_PSP_DEFERREDRENDER
	// These buffers are back to front in deferred mode as we're running a frame behind
	if( !s_bDeferredImmediateDraw )
	{
		u_int uTmp = s_uCurrentDisplayedBuffer;
		s_uCurrentDisplayedBuffer = s_uCurrentRenderingBuffer;
		s_uCurrentRenderingBuffer = uTmp;
	}
#endif
}

void Asura_PSP_Graphics::SetEnableDeferredImmediateDraw( bool bEnable )
{ 
#ifdef ASURA_PSP_DEFERREDRENDER
	// If we're going to draw immediately then make sure the GU is idle
	if( bEnable && !s_bDeferredImmediateDraw )
	{
		GU_Sync( SCEGU_SYNC_FINISH, SCEGU_SYNC_WAIT );
	}

	s_bDeferredImmediateDraw = bEnable;
#endif
}

void Asura_PSP_Graphics::EndFrame(void)
{
	//Asura_PSP_Fog::OnEndFrame();					// AD : Fog::OnEndFrame call has been moved into project specific render
													//		*before* zbuffer clear, so that z buffer fog works. If using HW
													//		fog, this call is unnecessary anyway..
	Asura_PSP_VRAMManagement::OnEndFrame(); 

	Asura_PSP_MotionBlur::Render();

	if(dbg_bShowGuardbandArea)
	{
		// KOR - 07/03/2006 - Render a rectangle showing the limits of the view frustum
		Asura_PSP_RenderStates::RequestColourMaterial(0);
		Asura_PSP_TextureManagement::RequestTextureIndex( ASURA_TEXTURE_NONE );
		Asura_Render::CurrentStates.RequestZBufferEnabled(false);
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
		GU_Material(SCEGU_AMBIENT, 0x20ffffff);
		GU_SpriteMode( 1, 1, dbg_fShowGuardbandAreaZoomIn * 480.f * 480.f / 4096.f, dbg_fShowGuardbandAreaZoomIn * 272.f * 272.f / 4096.f);
		GU_DrawSprite(480.f * (2048.f - 240.f * dbg_fShowGuardbandAreaZoomIn) / 4096.f, 272.f * (2048.f - 136.f * dbg_fShowGuardbandAreaZoomIn) / 4096.f, 0, 0, 0, 0, 0);
	}

#ifdef ASURA_PSP_PROFILING
	Asura_PSP_Profiling::Signal( Asura_PSP_Profiling::ASURA_PSP_SIGNAL_SYNC ); 
	ASURA_CPU_PROFILE__STOP(ASURA_CPU_PROFILE__TOTALFRAME);
	Asura_PSP_Profiling::Signal( Asura_PSP_Profiling::ASURA_PSP_SIGNAL_PROFILE_START ); 
	Asura_PSP_Profiling::Render(); // Ensure this is last thing rendered
	Asura_PSP_Profiling::ClearValues();
	Asura_PSP_Profiling::Signal( Asura_PSP_Profiling::ASURA_PSP_SIGNAL_PROFILE_END ); 
	Asura_PSP_Profiling::Signal( Asura_PSP_Profiling::ASURA_PSP_SIGNAL_IDLE_START ); 
	ASURA_CPU_PROFILE__START_LABEL(ASURA_CPU_PROFILE__TOTALFRAME, "Total frame");
#endif //#ifdef ASURA_PSP_PROFILING

    // (NY - 19.06.2007) removed the assert that was here, as it never rendered and just infinitely looped instead - I assume the assert renderer actually calls this function :)
	if (GU_CheckList() > ASURA_PSP_DISPLAY_LIST_SIZE)
	{
		Asura_OutputToDebugger_Retail( ( "Display list overflowed (%d bytes needed)!!!\n", GU_CheckList()) );
		// light up GPO light 5
		Asura_PSP_GPIO::SetGPO((1 << 5));
		//just infinite loop here, if we've overflowed a crash is likely anyway
		while(true)
		{
			//keep callbacks happy, for what it's worth....
			sceKernelDelayThreadCB(1000);
		}
	}
	else if ((ASURA_PSP_DISPLAY_LIST_SIZE - GU_CheckList()) < 0x1000)
	{
		Asura_OutputToDebugger( ( "Display list critically low (%d bytes needed).\n", GU_CheckList()) );
	}

	uint32 uDisplayListSize = GU_Finish();

#ifndef ASURA_PSP_SONY_SUBMISSION
	if (uDisplayListSize > s_uMaxDisplayList)
	{
		s_uMaxDisplayList = uDisplayListSize;
	}
#endif // #ifndef ASURA_PSP_SONY_SUBMISSION

#if defined(ASURA_USE_SCREENSHOT_SYSTEM)
	Asura_ScreenShot_System::DrawBufferIsReady	();
#endif	// defined(ASURA_USE_SCREENSHOT_SYSTEM)

#ifndef ASURA_PSP_SYNC_ON_BEGINFRAME
#ifdef ASURA_PSP_DEFERREDRENDER
	if( !s_bDeferredImmediateDraw )
#endif
	{
		VSync(); 
	}
#endif//!ASURA_PSP_SYNC_ON_BEGINFRAME

#ifdef ASURA_PSP_DEFERREDRENDER
	// Make sure none of our display list is residing in the cache
	sceKernelDcacheWritebackRange( g_auDisplayList[ s_uCurrentDisplayList ], uDisplayListSize );

	// Start this frame's display list drawing (GPU) whilst the next frame executes (CPU)
	GU_SendList( SCEGU_QUEUE_TAIL, g_auDisplayList[ s_uCurrentDisplayList ], NULL, 0, NULL );

	// If this flag is set then we're effectively emulating immediate mode
	if( s_bDeferredImmediateDraw ) { VSync(); }

	// Swap display lists
	s_uCurrentDisplayList = 1 - s_uCurrentDisplayList;
#endif

	UnlockMutex();
}

void Asura_PSP_Graphics::SetupEntityAlphaStates(u_int uMaterialFlags, u_int uTextureFlags, bool bForceTranslucent /* = false */, uint32 uProjectMaterialFlags /* = 0 */)
{
	if(uMaterialFlags & AsuraMaterialFlag_Transparent || bForceTranslucent)
	{
		// Not sure if we want z-writes on or off... 
		// (JG - 31.05.2007): ... so let them decide
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled( s_bTransparentEntityWriteToZBuffer );

		Asura_Render::CurrentStates.RequestAlphaTest(true);
		if (uTextureFlags & AsuraTextureFlag_8BitAlpha || bForceTranslucent)
		{
			Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
		}
		else
		{
			Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);
		}
	}
	else if(uMaterialFlags & AsuraMaterialFlag_PS2AdditiveAlpha)
	{
		// Glowing - no z-write.
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);		
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_GLOWING);
		Asura_Render::CurrentStates.RequestAlphaTest(true);
	}
	else if(uMaterialFlags & AsuraMaterialFlag_SelfIlluminate)
	{
		// Same as solid.
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);
		// Don't use the alpha - it's just for self-illumination
		Asura_Render::CurrentStates.RequestAlphaTest(false);
	}
	else
	{
		// Completely solid.
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);
		Asura_Render::CurrentStates.RequestAlphaTest(false);
	}

	// Centralised state override
	DoStateOverride( uProjectMaterialFlags );
}

DEBUGVAR bool dbg_bUseEntityLights = true;

void Asura_PSP_Graphics::SetupEntityLights( const float fAlpha , bool bObjectInWorldSpace /* = false */ )
{
	if (dbg_bUseEntityLights)
	{
		// New SH stuff
		Asura_SphericalHarmonics_Order2_RGB& xSH = Asura_GI_Solution::s_xSHForCurrentRender;
		{
			Asura_Vector_3 xLightDirR(xSH.m_afRedCoefficients[1], xSH.m_afRedCoefficients[2], xSH.m_afRedCoefficients[3]);
			Asura_Vector_3 xLightDirG(xSH.m_afGreenCoefficients[1], xSH.m_afGreenCoefficients[2], xSH.m_afGreenCoefficients[3]);
			Asura_Vector_3 xLightDirB(xSH.m_afBlueCoefficients[1], xSH.m_afBlueCoefficients[2], xSH.m_afBlueCoefficients[3]);

			const int iMagnitudeR = static_cast<int>(Asura_Maths::Min(xLightDirR.Normalise(), 1.f) * 0xff);
			const int iMagnitudeG = static_cast<int>(Asura_Maths::Min(xLightDirG.Normalise(), 1.f) * 0xff);
			const int iMagnitudeB = static_cast<int>(Asura_Maths::Min(xLightDirB.Normalise(), 1.f) * 0xff);

			const int iAmbientR = static_cast<int>(Asura_Maths::Min(xSH.m_afRedCoefficients[0]	, 1.f) * 0xff);
			const int iAmbientG = static_cast<int>(Asura_Maths::Min(xSH.m_afGreenCoefficients[0], 1.f) * 0xff);
			const int iAmbientB = static_cast<int>(Asura_Maths::Min(xSH.m_afBlueCoefficients[0]	, 1.f) * 0xff);

			u_int uAmbientABGR = Asura_Maths::FloatToInt(fAlpha * 255.0f) << 24;
			// KOR - 21/10/2005 - New high contrast setting.
			// The PSP is incapable of true 2nd order spherical harmonic calculation. Ideally it should do this:
			//
			// Light intensity = (direction light brightness) * (Normal.Incidence) + Ambient
			//
			// Unfortunately, it can only do:
			//
			// Light intensity = (direction light brightness) * Min(0, (Normal.Incidence)) + Ambient
			// 
			// This tends to look rather flat, so I've to gain contrast I've rejigged the input to give the correct 
			// values at the extremes. The lighting at a normal orthogonal to the light is now incorrect though.
			// Both MV and Bond seem to like this better, so I'll leave it on by default.
			//
			// (AW - 01/11/05) - Bond artists have changed their minds. They like it on smooth skins but
			// not on object hierarchies. I've added accessor functions so projects can change their lighting
			// method at will.
			//
			// (KOR - 30/01/2006) - disabled by default as characters were blacking out.
			if(s_bUseHighContrastEntityLighting)
			{
				uAmbientABGR += Asura_Maths::ClampToRange(iAmbientR - iMagnitudeR, 0, 0xff) <<  0;
				uAmbientABGR += Asura_Maths::ClampToRange(iAmbientG - iMagnitudeG, 0, 0xff) <<  8;
				uAmbientABGR += Asura_Maths::ClampToRange(iAmbientB - iMagnitudeB, 0, 0xff) << 16;

				GU_LightColor(0, SCEGU_DIFFUSE, Asura_Maths::ClampToRange(iMagnitudeR + iAmbientR, 0, 0xff) <<  0);
				GU_LightColor(1, SCEGU_DIFFUSE, Asura_Maths::ClampToRange(iMagnitudeG + iAmbientG, 0, 0xff) <<  8);
				GU_LightColor(2, SCEGU_DIFFUSE, Asura_Maths::ClampToRange(iMagnitudeB + iAmbientB, 0, 0xff) << 16);
			}
			else
			{			
				uAmbientABGR += iAmbientR <<  0;
				uAmbientABGR += iAmbientG <<  8;
				uAmbientABGR += iAmbientB << 16;

				GU_LightColor(0, SCEGU_DIFFUSE, iMagnitudeR <<  0);
				GU_LightColor(1, SCEGU_DIFFUSE, iMagnitudeG <<  8);
				GU_LightColor(2, SCEGU_DIFFUSE, iMagnitudeB << 16);
			}
			Asura_PSP_RenderStates::RequestAmbient(uAmbientABGR);

			if(!bObjectInWorldSpace)
			{
				// KOR - 3/3/2006 - entity renderers now premultiply view matrix
				// into world matrix, to get around PSP's 24-bit precision rubbishness.
				// Therefore we have to premultiply the light directions along with it.
				const Asura_Matrix_3x3& xViewOri = *Asura_Camera::GetOrientation();
				xLightDirR = xViewOri.ApplyTranspose(xLightDirR);
				xLightDirG = xViewOri.ApplyTranspose(xLightDirG);
				xLightDirB = xViewOri.ApplyTranspose(xLightDirB);
				// PSP axes are different from Asura ones for some reason, so we have to flip Y and Z.
				// TODO: surely if we use axes consistently we shouldn't have to do this.
				xLightDirR.y = -xLightDirR.y;
				xLightDirR.z = -xLightDirR.z;
				xLightDirG.y = -xLightDirG.y;
				xLightDirG.z = -xLightDirG.z;
				xLightDirB.y = -xLightDirB.y;
				xLightDirB.z = -xLightDirB.z;
			}
			GU_Light(0, SCEGU_LIGHT_DIRECTION, SCEGU_DIFFUSE, reinterpret_cast<Asura_PSP_Vector3*>(&xLightDirR));
			GU_Light(1, SCEGU_LIGHT_DIRECTION, SCEGU_DIFFUSE, reinterpret_cast<Asura_PSP_Vector3*>(&xLightDirG));
			GU_Light(2, SCEGU_LIGHT_DIRECTION, SCEGU_DIFFUSE, reinterpret_cast<Asura_PSP_Vector3*>(&xLightDirB));


			SetUseLighting(true);
			SetNumberOfLights(3);
		}
	}
	// JG - 03/01/07: Moved set material type out of the dbg_bUseEntityLights condition to set material type for 
	// objects even if we are not using entity lights, otherwise they get rendered with the last material color used
	// when entity lights go off
	SetMaterialType(ASURA_PSP_GRAPHICS_MATERIALTYPE_ENTITY);
}

void Asura_PSP_Graphics::SetNumberOfLights(u_int uNumLights)
{
	if (s_uNumLights == uNumLights) return;

	Asura_Assert(uNumLights <= 4, "Too many lights");
	// There should be no calls to GU_[En|Dis]able(SCEGU_LIGHT*) outside this function, to maintain state.
	if(uNumLights > s_uNumLights)
	{
		// Turn on required lights
		for(u_int uLight = s_uNumLights; uLight < uNumLights; ++uLight)
		{
			GU_Enable(SCEGU_LIGHT0 + uLight);
		}
	}
	else if(uNumLights < s_uNumLights)
	{
		// Turn off required lights
		ScePspFVector3* pxDummyVector = (ScePspFVector3*)(&Asura_Maths::ZeroVector3);
		
		for(u_int uLight = uNumLights; uLight < s_uNumLights; ++uLight)
		{
			GU_Disable(SCEGU_LIGHT0 + uLight);
	
			// KJFL - 28 February 2005 - even when disabled, directional lights are quicker than point lights!?
			GU_Light(uLight, SCEGU_LIGHT_DIRECTION, SCEGU_DIFFUSE, pxDummyVector);
		}
	}
	s_uNumLights = uNumLights;
}

void Asura_PSP_Graphics::SetUseLighting(bool bUseLighting)
{
	// There should be no calls to GU_[En|Dis]able(SCEGU_LIGHTING) outside this function, to maintain state.
	if(bUseLighting == s_bUseLighting) return;
	if(bUseLighting)
	{
		GU_Enable(SCEGU_LIGHTING);
	}
	else
	{
		GU_Disable(SCEGU_LIGHTING);
	}
	s_bUseLighting = bUseLighting;
}

void Asura_PSP_Graphics::SetMaterialType(ASURA_PSP_GRAPHICS_MATERIALTYPE eType, u_int uAmbientColour /* = 0xffffffff */)
{
	// There should be no calls to GU_Material or Asura_PSP_RenderStates::RequestColourMaterial outside this function, to maintain state.
    // (MH - 19.12.2006) it seems that there are. The simplest solution is to require such code to call InvalidateMaterialType().
	if(eType == s_eMaterialType) 
	{
		return;
	}

	switch(eType)
	{
		case ASURA_PSP_GRAPHICS_MATERIALTYPE_ENTITY:
			Asura_PSP_RenderStates::RequestColourMaterial(0);// Use all values below
			GU_Material(SCEGU_AMBIENT, 0xffffffff);// Multiplied by ambient lighting colour.
			GU_Material(SCEGU_DIFFUSE, 0xffffffff);// Multiplied by diffuse lighting colour, then added to previous result
			GU_Material(SCEGU_SPECULAR, 0xffffffff);// Not currently used
			break;
		case ASURA_PSP_GRAPHICS_MATERIALTYPE_ENV:
			Asura_PSP_RenderStates::RequestColourMaterial(SCEGU_AMBIENT);// Use ambient from vertex colour
			GU_Material(SCEGU_DIFFUSE, 0xffffffff);
			GU_Material(SCEGU_SPECULAR, 0xffffffff);
			break;
		case ASURA_PSP_GRAPHICS_MATERIALTYPE_SHADOW:
			Asura_PSP_RenderStates::RequestColourMaterial(0);
			GU_Material(SCEGU_AMBIENT, 0xff000000);
			GU_Material(SCEGU_DIFFUSE, 0xffffffff);
			GU_Material(SCEGU_SPECULAR, 0xffffffff);
			break;
		case ASURA_PSP_GRAPHICS_MATERIALTYPE_SKYBOX:
			Asura_PSP_RenderStates::RequestColourMaterial(0);
			GU_Material(SCEGU_AMBIENT, uAmbientColour);
			GU_Material(SCEGU_DIFFUSE, 0xffffffff);
			GU_Material(SCEGU_SPECULAR, 0xffffffff);
			break;
		case ASURA_PSP_GRAPHICS_MATERIALTYPE_MANUAL:
			// the user is going to manually set these states
			break; 
		case ASURA_PSP_GRAPHICS_MATERIALTYPE_NONE:
		default:
			Asura_Assert(0, "Invalid material type");
	}
	s_eMaterialType = eType;
}

void Asura_PSP_Graphics::SetUseColourDoubling(bool bDouble)
{
	if (bDouble != s_bUseColourDoubling)
	{
		s_bUseColourDoubling = bDouble;
		if (bDouble)
		{
			GU_Enable(SCEGU_COLOR_DOUBLE);
		}
		else
		{
			GU_Disable(SCEGU_COLOR_DOUBLE);
		}
	}
}

void Asura_PSP_Graphics::SetTransparentEntityWriteToZBuffer(bool bTransparentEntityWriteToZBuffer)
{
	s_bTransparentEntityWriteToZBuffer = bTransparentEntityWriteToZBuffer; 
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_PSP_Graphics::SetupPSPProjectionMatrix
// Author       : Andy Tate
// Date         : 14-Jun-2005
//
// Description  : Sets the PSP projection matrix based on the specified FOV.
//
// Parameters   : The field-of-view angle to use and the offset to apply to the
//				  near clipping-plane.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_PSP_Graphics::SetupPSPProjectionMatrix (const float fFOVInRadians,
												   const float fNearPlaneOffset)
{
	const Asura_Vector_2&	xSubPixelOffset	= Asura_Render::GetSubPixelOffset ();
	const ScePspFVector3	xTranslation	= {xSubPixelOffset.x / (SCEGU_SCR_WIDTH / 2), -xSubPixelOffset.y / (SCEGU_SCR_HEIGHT / 2), 0.0f};
	const float				fNearClipPlane	= Asura_Camera::GetNearClip() + fNearPlaneOffset;
	const float				fFarClipPlane	= Asura_Camera::GetFarClip();

	// Set the projection matrix on the stack...
	GUM_MatrixMode	(SCEGU_MATRIX_PROJECTION);
	GUM_LoadIdentity	();
	GUM_Translate		(&xTranslation);
	GUM_Perspective	(fFOVInRadians, SCEGU_SCR_ASPECT, fNearClipPlane, fFarClipPlane);


	{
		Asura_PSP_Matrix4 xMatrix;
		GUM_StoreMatrix(&xMatrix);
		if(dbg_bShowGuardbandArea)
		{
			xMatrix.x.x *= 480.f / 4096.f;
			xMatrix.y.y *= 272.f / 4096.f;
			if (dbg_fShowGuardbandAreaZoomIn > 1.0f)
			{
				xMatrix.x.x *= dbg_fShowGuardbandAreaZoomIn;
				xMatrix.y.y *= dbg_fShowGuardbandAreaZoomIn; 
			}
		}

		///////////////////////////////////////////////////////////////////////////
		// The perspective matrix is as follows:
		//
		// | 1/tan(u)     0         0            0      |
		// |    0     1/tan(v)      0            0      |
		// |    0         0    (F+N)/(F-N) (-2FN)/(F-N) |
		// |    0         0         1            0      |
		//
		// Kim's hack assumes the far-clip tends to infinity and therefore the 
		// ZZ element tends to 1, this also means that ZW tends towards -2 so
		// Juan and I have added this modification. Unfortunately, the value of
		// -2 causes the near plan to be too far into the screen so a slightly
		// smaller value is used for the sake of appearance.
		//
		// However, these hacks should not be necessary - the far clipping-plane
		// should be perfectly safe to set and use, the closer the far-clip the
		// more resolution there SHOULD be in the depth buffer. But something
		// odd seems to be happening here, the larger we set the far-clip value
		// the more resolution we appear to get - low far-clip values actually
		// look like they've got terrible small resolution depth-buffers.
		//
		// This needs further investigation, but we currently don't have time
		// to look closer.
		//
		// ART - 05/07/2006
		//
		// AJG - 24/01/07
		// I think the WZ value actually tends to -2N rather than -2. That means
		// previous hack value there was valid (N = 0.375) rather than just hacking
		// the hack. Changing the hack to use the value of N allows us to have
		// a near plane value that changes according to what the game needs in its
		// current state, rather than just some random value that's hoped to be
		// good enough for all projects.
		//
		// Obviously though, we still need to work out why the hacks are even
		// needed in the first place.
		///////////////////////////////////////////////////////////////////////////

		if(dbg_fPerspectiveMatrixZZHack != 0.0f)
		{
			xMatrix.z.z = dbg_fPerspectiveMatrixZZHack;
		}
		if(dbg_bUsePerspectiveMatrixWZHack != 0.0f)
		{
			xMatrix.w.z = -2.0f * fNearClipPlane;
		}
		GUM_LoadMatrix(&xMatrix);
	}

	GUM_UpdateMatrix();

}

/// End of function 'Asura_PSP_Graphics::SetupPSPProjectionMatrix'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_PSP_Graphics::SetupPSPProjectionMatrix
// Author       : Andy Tate
// Date         : 14-Jun-2005
//
// Description  : Sets the PSP projection matrix using the camera's FOV...
//
// Parameters	: The offset to apply to the near clipping-plane.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_PSP_Graphics::SetupPSPProjectionMatrix (const float fNearPlaneOffset)
{
	const float	fFOVInRadians	= Asura_Camera::GetVerticalFieldOfView ();


	// Simply call the other function using the camera's FOV...

	SetupPSPProjectionMatrix	(fFOVInRadians, fNearPlaneOffset);
}

/// End of function 'Asura_PSP_Graphics::SetupPSPProjectionMatrix'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_PSP_Graphics::SetupPSPTextureMatrix
// Author       : Catalin Arsenescu
// Date         : 06-03-2007
//
// Description  : Sets the PSP texture matrix
// The default sets it to Identity
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////
void Asura_PSP_Graphics::SetupPSPTextureMatrix(const Asura_PSP_Matrix4* pxMatrix, bool bUseprojectionMatrix)
{
	GUM_MatrixMode(SCEGU_MATRIX_TEXTURE);

	if(pxMatrix)
	{
		GUM_LoadMatrix(pxMatrix);
	}
	else
	{
		GUM_LoadIdentity();
	}

	if(bUseprojectionMatrix)
	{
		const float fFOVInRadians	= Asura_Camera::GetVerticalFieldOfView();
		const float	fNearClipPlane	= Asura_Camera::GetNearClip();
		const float	fFarClipPlane	= Asura_Camera::GetFarClip();

		GUM_Perspective(fFOVInRadians, SCEGU_SCR_ASPECT, fNearClipPlane, fFarClipPlane);
	}
}

// If a state override callback exists then call it
//
void Asura_PSP_Graphics::DoStateOverride( uint32 uFlags )
{
	if( s_pfnStateOverride ) { s_pfnStateOverride( uFlags ); }
}

#ifndef ASURA_PSP_SONY_SUBMISSION
void Asura_PSP_Graphics::VSync_ForAssert()
{
	GU_Sync( SCEGU_SYNC_FINISH, SCEGU_SYNC_WAIT );

#ifndef ASURA_PSP_SCEEGU
	if (s_bVsyncOn && !Asura_PSP_GPIO::GetGPI(ASURA_PSP_GPI_DISABLE_VSYNC))
	{
		sceDisplayWaitVblankStartCB();
	}
#endif

	SwapBuffers();

#ifdef ASURA_PSP_SCEEGU
	if (s_bVsyncOn && !Asura_PSP_GPIO::GetGPI(ASURA_PSP_GPI_DISABLE_VSYNC))
	{
		sceDisplayWaitVblankStartCB();
	}
#endif
}

void Asura_PSP_Graphics::BeginFrame_ForAssert()
{
	s_bInFrameForAssert = true;

#if defined(ASURA_PSP_SYNC_ON_BEGINFRAME)
	VSync_ForAssert();
#endif	// defined(ASURA_PSP_SYNC_ON_BEGINFRAME)

#ifdef ASURA_PSP_DEFERREDRENDER
	GU_Start( SCEGU_LIST, g_auDisplayList[ s_uCurrentDisplayList ], ASURA_PSP_DISPLAY_LIST_SIZE );
	GU_DrawBufferList( SCEGU_PF8888, s_uCurrentRenderingBuffer, SCEGU_VRAM_WIDTH );
#else
	GU_Start( SCEGU_IMMEDIATE, g_auDisplayList[ 0 ], ASURA_PSP_DISPLAY_LIST_SIZE );
#endif

	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(0); // Z-write.
	GU_ClearColor(0x00000000); 
	GU_Clear(SCEGU_CLEAR_FASTALL);
	GU_DebugBegin();
}

void Asura_PSP_Graphics::EndFrame_ForAssert()
{
	GU_DebugEnd();
	uint32 uDisplayListSize = GU_Finish();

#if !defined(ASURA_PSP_SYNC_ON_BEGINFRAME)
	VSync_ForAssert();
#endif	// !defined(ASURA_PSP_SYNC_ON_BEGINFRAME)

#ifdef ASURA_PSP_DEFERREDRENDER
	sceKernelDcacheWritebackRange( g_auDisplayList[ s_uCurrentDisplayList ], uDisplayListSize );

	// Start this frame's display list drawing (GPU) whilst the next frame executes (CPU)
	GU_SendList( SCEGU_QUEUE_TAIL, g_auDisplayList[ s_uCurrentDisplayList ], NULL, 0, NULL );

	// Swap display lists
	s_uCurrentDisplayList = 1 - s_uCurrentDisplayList;
#endif

	s_bInFrameForAssert = false;
}

#endif //#ifndef ASURA_PSP_SONY_SUBMISSION

#include "Core/Asura_Multithread.h"
static Asura_Mutex g_xRenderingMutex;
void Asura_PSP_Graphics::LockMutex()
{
	g_xRenderingMutex.Lock();
}
void Asura_PSP_Graphics::UnlockMutex()
{
	g_xRenderingMutex.Unlock();
}

/// End of file. ///