// Copyright (C) 2003 Dolphin Project.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/


#include "Globals.h"
#include "Thread.h"
#include "Atomic.h"

#include <vector>
#include <cmath>
#include <cstdio>

#include "GLUtil.h"

#include <Cg/cg.h>
#include <Cg/cgGL.h>

#include "FileUtil.h"

#ifdef _WIN32
#include <mmsystem.h>
#endif

#include "CommonPaths.h"
#include "VideoConfig.h"
#include "Profiler.h"
#include "Statistics.h"
#include "ImageWrite.h"
#include "Render.h"
#include "OpcodeDecoding.h"
#include "BPStructs.h"
#include "TextureMngr.h"
#include "RasterFont.h"
#include "VertexShaderGen.h"
#include "DLCache.h"
#include "PixelShaderCache.h"
#include "PixelShaderManager.h"
#include "VertexShaderCache.h"
#include "VertexShaderManager.h"
#include "VertexLoaderManager.h"
#include "VertexLoader.h"
#include "PostProcessing.h"
#include "TextureConverter.h"
#include "XFB.h"
#include "OnScreenDisplay.h"
#include "Timer.h"
#include "StringUtil.h"
#include "FramebufferManager.h"
#include "Fifo.h"

#include "main.h" // Local
#ifdef _WIN32
#include "OS/Win32.h"
#include "AVIDump.h"
#endif

#if defined(HAVE_WX) && HAVE_WX
#include <wx/image.h>
#endif

// Declarations and definitions
// ----------------------------
int s_fps=0;

CGcontext g_cgcontext;
CGprofile g_cgvProf;
CGprofile g_cgfProf;

RasterFont* s_pfont = NULL;

static bool s_bLastFrameDumped = false;
#ifdef _WIN32
static bool s_bAVIDumping = false;
#else
static FILE* f_pFrameDump;
#endif

// 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA.
static int s_MSAASamples = 1;
static int s_MSAACoverageSamples = 0;

bool s_bHaveFramebufferBlit = false; // export to FramebufferManager.cpp
static bool s_bHaveCoverageMSAA = false;
static u32 s_blendMode;

static volatile bool s_bScreenshot = false;
#if defined(HAVE_WX) && HAVE_WX
static Common::Thread *scrshotThread = 0;
#endif
static Common::CriticalSection s_criticalScreenshot;
static std::string s_sScreenshotName;

int frameCount;

// The custom resolution
// TODO: Add functionality to reinit all the render targets when the window is resized.
static int m_CustomWidth;
static int m_CustomHeight;
// The framebuffer size
static int m_FrameBufferWidth;
static int m_FrameBufferHeight;

static GLuint s_tempScreenshotFramebuffer = 0;

static unsigned int s_XFB_width;
static unsigned int s_XFB_height;

static float xScale;
static float yScale;

static int EFBxScale;
static int EFByScale;

static bool s_skipSwap = false;

#ifndef _WIN32
int OSDChoice = 0 , OSDTime = 0, OSDInternalW = 0, OSDInternalH = 0;
#endif

namespace {

#if defined(HAVE_WX) && HAVE_WX
// Screenshot thread struct
typedef struct
{
	int W, H;
	std::string filename;
	wxImage *img;
} ScrStrct;
#endif

static const GLenum glSrcFactors[8] =
{
	GL_ZERO,
	GL_ONE,
	GL_DST_COLOR,
	GL_ONE_MINUS_DST_COLOR,
	GL_SRC_ALPHA,
	GL_ONE_MINUS_SRC_ALPHA, 
	GL_DST_ALPHA,
	GL_ONE_MINUS_DST_ALPHA
};

static const GLenum glDestFactors[8] = {
	GL_ZERO, 
	GL_ONE, 
	GL_SRC_COLOR, 
	GL_ONE_MINUS_SRC_COLOR,
	GL_SRC_ALPHA, 
	GL_ONE_MINUS_SRC_ALPHA,  
	GL_DST_ALPHA, 
	GL_ONE_MINUS_DST_ALPHA
};

static const GLenum glCmpFuncs[8] = {
	GL_NEVER,
	GL_LESS,
	GL_EQUAL,
	GL_LEQUAL,
	GL_GREATER,
	GL_NOTEQUAL,
	GL_GEQUAL,
	GL_ALWAYS
};

static const GLenum glLogicOpCodes[16] = {
	GL_CLEAR,
	GL_AND,
	GL_AND_REVERSE,
	GL_COPY,
	GL_AND_INVERTED,
	GL_NOOP,
	GL_XOR, 
	GL_OR,
	GL_NOR,
	GL_EQUIV,
	GL_INVERT,
	GL_OR_REVERSE,
	GL_COPY_INVERTED,
	GL_OR_INVERTED,
	GL_NAND,
	GL_SET
};

void SetDefaultRectTexParams()
{
	// Set some standard texture filter modes.
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	if (glGetError() != GL_NO_ERROR) {
		glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
		GL_REPORT_ERRORD();
	}
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}

void HandleCgError(CGcontext ctx, CGerror err, void* appdata)
{
	DEBUG_LOG(VIDEO, "Cg error: %s", cgGetErrorString(err));
	const char* listing = cgGetLastListing(g_cgcontext);
	if (listing != NULL) {
		DEBUG_LOG(VIDEO, "    last listing: %s", listing);
	}
}

} // namespace
void VideoConfig::UpdateProjectionHack()
{
	::UpdateProjectionHack(g_Config.iPhackvalue);
}


// Init functions
bool Renderer::Init()
{
	UpdateActiveConfig();
	bool bSuccess = true;
	s_blendMode = 0;
	s_MSAACoverageSamples = 0;
	switch (g_ActiveConfig.iMultisampleMode)
	{
		case MULTISAMPLE_OFF: s_MSAASamples = 1; break;
		case MULTISAMPLE_2X:  s_MSAASamples = 2; break;
		case MULTISAMPLE_4X:  s_MSAASamples = 4; break;
		case MULTISAMPLE_8X:  s_MSAASamples = 8; break;
		case MULTISAMPLE_CSAA_8X:   s_MSAASamples = 4; s_MSAACoverageSamples = 8; break;
		case MULTISAMPLE_CSAA_8XQ:  s_MSAASamples = 8; s_MSAACoverageSamples = 8; break;
		case MULTISAMPLE_CSAA_16X:  s_MSAASamples = 4; s_MSAACoverageSamples = 16; break;
		case MULTISAMPLE_CSAA_16XQ: s_MSAASamples = 8; s_MSAACoverageSamples = 16; break;
		default:
		s_MSAASamples = 1;
	}
	GLint numvertexattribs = 0;
	g_cgcontext = cgCreateContext();

	cgGetError();
	cgSetErrorHandler(HandleCgError, NULL);

	// Look for required extensions.
	const char *ptoken = (const char*)glGetString(GL_EXTENSIONS);
	if (!ptoken)
	{
		PanicAlert("Your OpenGL Driver seems to be not working.\n"
				   "Please make sure your drivers are up-to-date and\n"
				   "that your video hardware is OpenGL 2.x compatible "
					);
		return false;
	}

	INFO_LOG(VIDEO, "Supported OpenGL Extensions:");
	INFO_LOG(VIDEO, ptoken);  // write to the log file
	INFO_LOG(VIDEO, "");

	OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s", (const char*)glGetString(GL_VENDOR), 
															 (const char*)glGetString(GL_RENDERER), 
															 (const char*)glGetString(GL_VERSION)).c_str(), 5000);

	glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &numvertexattribs);
	if (numvertexattribs < 11) {
		ERROR_LOG(VIDEO, "*********\nGPU: OGL ERROR: Number of attributes %d not enough\nGPU: *********Does your video card support OpenGL 2.x?", numvertexattribs);
		bSuccess = false;
	}

	// Init extension support.
	if (glewInit() != GLEW_OK) {
		ERROR_LOG(VIDEO, "glewInit() failed!Does your video card support OpenGL 2.x?");
		return false;
	}
	if (!GLEW_EXT_framebuffer_object) {
		ERROR_LOG(VIDEO, "*********\nGPU: ERROR: Need GL_EXT_framebufer_object for multiple render targets\nGPU: *********Does your video card support OpenGL 2.x?");
		bSuccess = false;
	}
	if (!GLEW_EXT_secondary_color) {
		ERROR_LOG(VIDEO, "*********\nGPU: OGL ERROR: Need GL_EXT_secondary_color\nGPU: *********Does your video card support OpenGL 2.x?");
		bSuccess = false;
	}
	s_bHaveFramebufferBlit = strstr(ptoken, "GL_EXT_framebuffer_blit") != NULL;
	if (!s_bHaveFramebufferBlit)
	{
		// MSAA ain't gonna work. turn it off if enabled.
		s_MSAASamples = 1;
	}
	s_bHaveCoverageMSAA = strstr(ptoken, "GL_NV_framebuffer_multisample_coverage") != NULL;
	if (!s_bHaveCoverageMSAA)
	{
		s_MSAACoverageSamples = 0;
	}

	if (!bSuccess)
		return false;

	// Handle VSync on/off
#if defined USE_WX && USE_WX
	// TODO: FILL IN
#elif defined _WIN32
	if (WGLEW_EXT_swap_control)
		wglSwapIntervalEXT(g_ActiveConfig.bVSync ? 1 : 0);
	else
		ERROR_LOG(VIDEO, "no support for SwapInterval (framerate clamped to monitor refresh rate)Does your video card support OpenGL 2.x?");
#elif defined(HAVE_X11) && HAVE_X11
	if (glXSwapIntervalSGI)
		glXSwapIntervalSGI(g_ActiveConfig.bVSync ? 1 : 0);
	else
		ERROR_LOG(VIDEO, "no support for SwapInterval (framerate clamped to monitor refresh rate)");
#endif

	// check the max texture width and height
	GLint max_texture_size;
	glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&max_texture_size);
	if (max_texture_size < 1024) {
		ERROR_LOG(VIDEO, "GL_MAX_TEXTURE_SIZE too small at %i - must be at least 1024", max_texture_size);
	}

	if (GL_REPORT_ERROR() != GL_NO_ERROR)
		bSuccess = false;

	if (glDrawBuffers == NULL && !GLEW_ARB_draw_buffers)
		glDrawBuffers = glDrawBuffersARB;
	
	if (!GLEW_ARB_texture_non_power_of_two) {
		WARN_LOG(VIDEO, "ARB_texture_non_power_of_two not supported.");
	}

	// Decide frambuffer size
	int W = (int)OpenGL_GetBackbufferWidth(), H = (int)OpenGL_GetBackbufferHeight();

	s_XFB_width = MAX_XFB_WIDTH;
	s_XFB_height = MAX_XFB_HEIGHT;

	TargetRectangle dst_rect;
	ComputeDrawRectangle(W, H, false, &dst_rect);

	xScale = 1.0f;
	yScale = 1.0f;

	if(!g_ActiveConfig.bNativeResolution)
	{
		if (g_ActiveConfig.b2xResolution)
		{
			xScale = 2.0f;
			yScale = 2.0f;
		}
		else
		{
			xScale = (float)(dst_rect.right - dst_rect.left) / (float)s_XFB_width;
			yScale = (float)(dst_rect.bottom - dst_rect.top) / (float)s_XFB_height;
		}
	}
	
	
	EFBxScale = ceilf(xScale);
	EFByScale = ceilf(yScale);

	m_FrameBufferWidth  = EFB_WIDTH * EFBxScale;
	m_FrameBufferHeight = EFB_HEIGHT * EFByScale;

	// Save the custom resolution
	m_CustomWidth = W;
	m_CustomHeight = H;

	// Because of the fixed framebuffer size we need to disable the resolution options while running
	g_Config.bRunning = true;

	if (GL_REPORT_ERROR() != GL_NO_ERROR)
		bSuccess = false;

	// Initialize the FramebufferManager
	g_framebufferManager.Init(m_FrameBufferWidth, m_FrameBufferHeight, s_MSAASamples, s_MSAACoverageSamples);

	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);

	if (GL_REPORT_ERROR() != GL_NO_ERROR)
		bSuccess = false;

	s_pfont = new RasterFont();

	// load the effect, find the best profiles (if any)
	if (cgGLIsProfileSupported(CG_PROFILE_ARBVP1) != CG_TRUE) {
		ERROR_LOG(VIDEO, "arbvp1 not supported");
		return false;
	}

	if (cgGLIsProfileSupported(CG_PROFILE_ARBFP1) != CG_TRUE) {
		ERROR_LOG(VIDEO, "arbfp1 not supported");
		return false;
	}

	g_cgvProf = cgGLGetLatestProfile(CG_GL_VERTEX);
	g_cgfProf = cgGLGetLatestProfile(CG_GL_FRAGMENT);
#if CG_VERSION_NUM == 2100
	// A bug was introduced in Cg2.1's handling of very large profile option values
	// so this will not work on ATI. ATI returns MAXINT = 2147483647 (0x7fffffff)
	// which is correct in OpenGL but Cg fails to handle it properly. As a result
	// -1 is used by Cg resulting (signedness incorrect) and compilation fails.
	if (strstr((const char*)glGetString(GL_VENDOR), "ATI") == NULL) 
	{
		cgGLSetOptimalOptions(g_cgvProf);
		cgGLSetOptimalOptions(g_cgfProf);
	}
#else
	cgGLSetOptimalOptions(g_cgvProf);
	cgGLSetOptimalOptions(g_cgfProf);
#endif

	INFO_LOG(VIDEO, "Max buffer sizes: %d %d", cgGetProgramBufferMaxSize(g_cgvProf), cgGetProgramBufferMaxSize(g_cgfProf));
	int nenvvertparams, nenvfragparams, naddrregisters[2];
	glGetProgramivARB(GL_VERTEX_PROGRAM_ARB,   GL_MAX_PROGRAM_ENV_PARAMETERS_ARB,    (GLint *)&nenvvertparams);
	glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB,    (GLint *)&nenvfragparams);
	glGetProgramivARB(GL_VERTEX_PROGRAM_ARB,   GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB, (GLint *)&naddrregisters[0]);
	glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB, (GLint *)&naddrregisters[1]);
	DEBUG_LOG(VIDEO, "Max program env parameters: vert=%d, frag=%d", nenvvertparams, nenvfragparams);
	DEBUG_LOG(VIDEO, "Max program address register parameters: vert=%d, frag=%d", naddrregisters[0], naddrregisters[1]);

	if (nenvvertparams < 238)
		ERROR_LOG(VIDEO, "Not enough vertex shader environment constants!!");

#ifndef _DEBUG
	cgGLSetDebugMode(GL_FALSE);
#endif
	
	glStencilFunc(GL_ALWAYS, 0, 0);
	glBlendFunc(GL_ONE, GL_ONE);

	glViewport(0, 0, GetTargetWidth(), GetTargetHeight()); // Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glShadeModel(GL_SMOOTH);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDepthFunc(GL_LEQUAL);
	
	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);  // 4-byte pixel alignment
	
	glDisable(GL_STENCIL_TEST);
	glEnable(GL_SCISSOR_TEST);

	glScissor(0, 0, GetTargetWidth(), GetTargetHeight());
	glBlendColorEXT(0, 0, 0, 0.5f);
	glClearDepth(1.0f);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	// legacy multitexturing: select texture channel only.
	glActiveTexture(GL_TEXTURE0);
	glClientActiveTexture(GL_TEXTURE0);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

	UpdateActiveConfig();
	return glGetError() == GL_NO_ERROR && bSuccess;
}

void Renderer::Shutdown(void)
{
	g_Config.bRunning = false;
	UpdateActiveConfig();
	delete s_pfont;
	s_pfont = 0;

	if (g_cgcontext) {
		cgDestroyContext(g_cgcontext);
		g_cgcontext = 0;
	}
	if(s_tempScreenshotFramebuffer)
		glDeleteFramebuffersEXT(1, &s_tempScreenshotFramebuffer);
	s_tempScreenshotFramebuffer = 0;

	g_framebufferManager.Shutdown();

#ifdef _WIN32
	if(s_bAVIDumping) {
		AVIDump::Stop();
	}
#else
	if(f_pFrameDump != NULL) {
		fclose(f_pFrameDump);
	}
#endif
}

// For the OSD menu's live resolution change
bool Renderer::Allow2x()
{
	if (GetFrameBufferWidth() >= 1280 && GetFrameBufferHeight() >= 960)
		return true;
	else
		return false;
}
bool Renderer::AllowCustom()
{
	if (GetCustomWidth() <= GetFrameBufferWidth() && GetCustomHeight() <= GetFrameBufferHeight())
		return true;
	else
		return false;
}

// Return the framebuffer size
int Renderer::GetFrameBufferWidth()
{
	return m_FrameBufferWidth;
}
int Renderer::GetFrameBufferHeight()
{
	return m_FrameBufferHeight;
}
// Return the custom resolution
int Renderer::GetCustomWidth()
{
	return m_CustomWidth;
}
int Renderer::GetCustomHeight()
{
	return m_CustomHeight;
}
// Return the rendering target width and height
int Renderer::GetTargetWidth()
{
	return  m_FrameBufferWidth;
}
int Renderer::GetTargetHeight()
{
	return m_FrameBufferHeight;
}
float Renderer::GetTargetScaleX()
{
	return EFBxScale;
}

float Renderer::GetTargetScaleY()
{
	return EFByScale;
}


float Renderer::GetXFBScaleX() { return xScale; }
float Renderer::GetXFBScaleY() { return yScale; }

TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
{
	return g_framebufferManager.ConvertEFBRectangle(rc);
}


void Renderer::ResetAPIState()
{
	// Gets us to a reasonably sane state where it's possible to do things like
	// image copies with textured quads, etc.
	VertexShaderCache::DisableShader();
	PixelShaderCache::DisableShader();
	glDisable(GL_SCISSOR_TEST);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	glDisable(GL_BLEND);
	glDepthMask(GL_FALSE);
	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}

void UpdateViewport();

void Renderer::RestoreAPIState()
{
	// Gets us back into a more game-like state.

	UpdateViewport();

	if (bpmem.genMode.cullmode > 0) glEnable(GL_CULL_FACE);
	if (bpmem.zmode.testenable)     glEnable(GL_DEPTH_TEST);
	if (bpmem.zmode.updateenable)   glDepthMask(GL_TRUE);

	glEnable(GL_SCISSOR_TEST);
	SetScissorRect();
	SetColorMask();
	SetBlendMode(true);

	VertexShaderCache::SetCurrentShader(0);
	PixelShaderCache::SetCurrentShader(0);
}

void Renderer::SetColorMask()
{
	GLenum ColorMask = (bpmem.blendmode.colorupdate) ? GL_TRUE : GL_FALSE;
	GLenum AlphaMask = (bpmem.blendmode.alphaupdate) ? GL_TRUE : GL_FALSE;
	glColorMask(ColorMask,  ColorMask,  ColorMask,  AlphaMask);
}

void Renderer::SetBlendMode(bool forceUpdate)
{	
	// blend mode bit mask
	// 0 - blend enable
	// 2 - reverse subtract enable (else add)
	// 3-5 - srcRGB function
	// 6-8 - dstRGB function

	u32 newval = bpmem.blendmode.subtract << 2;

	if (bpmem.blendmode.subtract) {
		newval |= 0x0049;   // enable blending src 1 dst 1
	} else if (bpmem.blendmode.blendenable) {
		newval |= 1;    // enable blending
		newval |= bpmem.blendmode.srcfactor << 3;
		newval |= bpmem.blendmode.dstfactor << 6;
	}
	
	u32 changes = forceUpdate ? 0xFFFFFFFF : newval ^ s_blendMode;

	if (changes & 1) {
		// blend enable change
		(newval & 1) ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
	}

	if (changes & 4) {
		// subtract enable change
		glBlendEquation(newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD);
	}

	if (changes & 0x1F8) {
		// blend RGB change
		glBlendFunc(glSrcFactors[(newval >> 3) & 7], glDestFactors[(newval >> 6) & 7]);
	}

	s_blendMode = newval;
}

u32 Renderer::AccessEFB(EFBAccessType type, int x, int y)
{
	if(!g_ActiveConfig.bEFBAccessEnable)
		return 0;

	// Get the rectangular target region covered by the EFB pixel.
	EFBRectangle efbPixelRc;
	efbPixelRc.left = x;
	efbPixelRc.top = y;
	efbPixelRc.right = x + 1;
	efbPixelRc.bottom = y + 1;

	TargetRectangle targetPixelRc = Renderer::ConvertEFBRectangle(efbPixelRc);

	// TODO (FIX) : currently, AA path is broken/offset and doesn't return the correct pixel
	switch (type)
	{

	case PEEK_Z:
	{
		if (s_MSAASamples > 1)
		{
			// Resolve our rectangle.
			g_framebufferManager.GetEFBDepthTexture(efbPixelRc);
			glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, g_framebufferManager.GetResolvedFramebuffer());
		}

		// Sample from the center of the target region.
		int srcX = (targetPixelRc.left + targetPixelRc.right) / 2;
		int srcY = (targetPixelRc.top + targetPixelRc.bottom) / 2;

		u32 z = 0;
		glReadPixels(srcX, srcY, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, &z);
		GL_REPORT_ERRORD();

		// Scale the 32-bit value returned by glReadPixels to a 24-bit
		// value (GC uses a 24-bit Z-buffer).
		// TODO: in RE0 this value is often off by one, which causes lighting to disappear
		return z >> 8;
	}

	case POKE_Z:
		// TODO: Implement
		break;

	case PEEK_COLOR: // GXPeekARGB
	{
		// Although it may sound strange, this really is A8R8G8B8 and not RGBA or 24-bit...

		// Tested in Killer 7, the first 8bits represent the alpha value which is used to
		// determine if we're aiming at an enemy (0x80 / 0x88) or not (0x70)
		// Wind Waker is also using it for the pictograph to determine the color of each pixel

		if (s_MSAASamples > 1)
		{
			// Resolve our rectangle.
			g_framebufferManager.GetEFBColorTexture(efbPixelRc);
			glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, g_framebufferManager.GetResolvedFramebuffer());
		}

		// Sample from the center of the target region.
		int srcX = (targetPixelRc.left + targetPixelRc.right) / 2;
		int srcY = (targetPixelRc.top + targetPixelRc.bottom) / 2;

		// Read back pixel in BGRA format, then byteswap to get GameCube's ARGB Format.
		u32 color = 0;
		glReadPixels(srcX, srcY, 1, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &color);
		GL_REPORT_ERRORD();

		return color;
	}

	case POKE_COLOR:
		// TODO: Implement. One way is to draw a tiny pixel-sized rectangle at
		// the exact location. Note: EFB pokes are susceptible to Z-buffering
		// and perhaps blending.
		//WARN_LOG(VIDEOINTERFACE, "This is probably some kind of software rendering");
		break;

	}

	return 0;
}

// Function: This function handles the OpenGL glScissor() function
// ----------------------------
// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
//		case 0x52 > SetScissorRect()
// ----------------------------
// bpmem.scissorTL.x, y = 342x342
// bpmem.scissorBR.x, y = 981x821
// Renderer::GetTargetHeight() = the fixed ini file setting
// donkopunchstania - it appears scissorBR is the bottom right pixel inside the scissor box
// therefore the width and height are (scissorBR + 1) - scissorTL
bool Renderer::SetScissorRect()
{
	int xoff = bpmem.scissorOffset.x * 2 - 342;
	int yoff = bpmem.scissorOffset.y * 2 - 342;
	float rc_left = (float)bpmem.scissorTL.x - xoff - 342; // left = 0
	if (rc_left < 0) rc_left = 0;

	float rc_top = (float)bpmem.scissorTL.y - yoff - 342; // right = 0
	if (rc_top < 0) rc_top = 0;
	
	float rc_right = (float)bpmem.scissorBR.x - xoff - 341; // right = 640
	if (rc_right > EFB_WIDTH) rc_right = EFB_WIDTH;
	

	float rc_bottom = (float)bpmem.scissorBR.y - yoff - 341; // bottom = 480
	if (rc_bottom > EFB_HEIGHT) rc_bottom = EFB_HEIGHT;

	if(rc_left > rc_right)
	{
		int temp = rc_right;
		rc_right = rc_left;
		rc_left = temp;
	}
	if(rc_top > rc_bottom)
	{
		int temp = rc_bottom;
		rc_bottom = rc_top;
		rc_top = temp;
	}

	
	// Check that the coordinates are good
	if (rc_right != rc_left && rc_bottom != rc_top)
	{
		glScissor(
			(int)(rc_left * EFBxScale), // x = 0 for example
			(int)((EFB_HEIGHT - rc_bottom) * EFByScale), // y = 0 for example
			(int)((rc_right - rc_left)* EFBxScale), // width = 640 for example
			(int)((rc_bottom - rc_top) * EFByScale) // height = 480 for example
			); 
		return true;
	}
	else
	{
		glScissor(
			0,
			0,
			Renderer::GetTargetWidth(), 
			Renderer::GetTargetHeight()
			);
	}
	return false;
}

void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z)
{
	
	// Update the view port for clearing the picture
	TargetRectangle targetRc = Renderer::ConvertEFBRectangle(rc);
	glViewport(targetRc.left, targetRc.bottom, targetRc.GetWidth(), targetRc.GetHeight());
	glScissor(targetRc.left, targetRc.bottom, targetRc.GetWidth(), targetRc.GetHeight());
	

	// Always set the scissor in case it was set by the game and has not been reset
	

	VertexShaderManager::SetViewportChanged();

	GLbitfield bits = 0;
	if (colorEnable)
	{
		bits |= GL_COLOR_BUFFER_BIT;
		glClearColor(
			((color >> 16) & 0xFF) / 255.0f,
			((color >> 8) & 0xFF) / 255.0f,
			(color & 0xFF) / 255.0f,
			((color >> 24) & 0xFF) / 255.0f
			);
	}
	if (zEnable)
	{
		bits |= GL_DEPTH_BUFFER_BIT;
		glClearDepth((z & 0xFFFFFF) / float(0xFFFFFF));
	}

	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glClear(bits);
	SetScissorRect();
}
static bool XFBWrited = false;
void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc)
{
	if(!fbWidth || !fbHeight)
		return;
	s_skipSwap = g_bSkipCurrentFrame;
	VideoFifo_CheckEFBAccess();
	VideoFifo_CheckSwapRequestAt(xfbAddr, fbWidth, fbHeight);
	XFBWrited = true;
	// XXX: Without the VI, how would we know what kind of field this is? So
	// just use progressive.
	if (g_ActiveConfig.bUseXFB)
	{
		g_framebufferManager.CopyToXFB(xfbAddr, fbWidth, fbHeight, sourceRc);
	}
	else
	{
		Renderer::Swap(xfbAddr, FIELD_PROGRESSIVE, fbWidth, fbHeight,sourceRc);
		Common::AtomicStoreRelease(s_swapRequested, FALSE);
	}
}

// This function has the final picture. We adjust the aspect ratio here.
void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,const EFBRectangle& Rc)
{
	if (g_bSkipCurrentFrame || (!XFBWrited && !g_ActiveConfig.bUseRealXFB) || !fbWidth || !fbHeight)
	{
		g_VideoInitialize.pCopiedToXFB(false);
		return;
	}
	if (field == FIELD_LOWER) xfbAddr -= fbWidth * 2;
	u32 xfbCount = 0;
	const XFBSource** xfbSourceList = g_framebufferManager.GetXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount);
	if ((!xfbSourceList || xfbCount == 0) && g_ActiveConfig.bUseXFB && !g_ActiveConfig.bUseRealXFB)
	{
		g_VideoInitialize.pCopiedToXFB(false);
		WARN_LOG(VIDEO, "Failed to get video for this frame");
		return;
	}

	DVSTARTPROFILE();

	ResetAPIState();

	TargetRectangle back_rc;
	ComputeDrawRectangle(m_CustomWidth, m_CustomHeight, true, &back_rc);

	// Make sure that the wireframe setting doesn't screw up the screen copy.
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	// Textured triangles are necessary because of post-processing shaders

	// Disable all other stages
	for (int i = 1; i < 8; ++i)
		TextureMngr::DisableStage(i);

	// Update GLViewPort
	glViewport(back_rc.left, back_rc.bottom, back_rc.GetWidth(), back_rc.GetHeight());

	GL_REPORT_ERRORD();

	// Copy the framebuffer to screen.

	// Render to the real buffer now.
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer

	// Texture map s_xfbTexture onto the main buffer
	glActiveTexture(GL_TEXTURE0);
	glEnable(GL_TEXTURE_RECTANGLE_ARB);
	// Use linear filtering.
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	// We must call ApplyShader here even if no post proc is selected - it takes
	// care of disabling it in that case. It returns false in case of no post processing.
	bool applyShader = PostProcessing::ApplyShader();

	const XFBSource* xfbSource = NULL;

	if(g_ActiveConfig.bUseXFB)
	{
		// draw each xfb source
		for (u32 i = 0; i < xfbCount; ++i)
		{
			xfbSource = xfbSourceList[i];

			TargetRectangle sourceRc;

			if (g_ActiveConfig.bAutoScale || g_ActiveConfig.bUseXFB)
			{
				sourceRc = xfbSource->sourceRc;
			}
			else
			{
				sourceRc.left = 0;
				sourceRc.top = xfbSource->texHeight;
				sourceRc.right = xfbSource->texWidth;
				sourceRc.bottom = 0;
			}

			MathUtil::Rectangle<float> drawRc;

			if (g_ActiveConfig.bUseXFB && !g_ActiveConfig.bUseRealXFB)
			{
				// use virtual xfb with offset
				int xfbHeight = xfbSource->srcHeight;
				int xfbWidth = xfbSource->srcWidth;
				int hOffset = ((s32)xfbSource->srcAddr - (s32)xfbAddr) / ((s32)fbWidth * 2);

				drawRc.top = 1.0f - (2.0f * (hOffset) / (float)fbHeight);
				drawRc.bottom = 1.0f - (2.0f * (hOffset + xfbHeight) / (float)fbHeight);
				drawRc.left = -(xfbWidth / (float)fbWidth);
				drawRc.right = (xfbWidth / (float)fbWidth);

				if (!g_ActiveConfig.bAutoScale)
				{
					// scale draw area for a 1 to 1 pixel mapping with the draw target
					float vScale = (float)fbHeight / (float)back_rc.GetHeight();
					float hScale = (float)fbWidth / (float)back_rc.GetWidth();

					drawRc.top *= vScale;
					drawRc.bottom *= vScale;
					drawRc.left *= hScale;
					drawRc.right *= hScale;
				}
			}
			else
			{
				drawRc.top = 1;
				drawRc.bottom = -1;
				drawRc.left = -1;
				drawRc.right = 1;
			}
			
			// Tell the OSD Menu about the current internal resolution
			OSDInternalW = xfbSource->sourceRc.GetWidth(); OSDInternalH = xfbSource->sourceRc.GetHeight();

			// Texture map xfbSource->texture onto the main buffer
			glBindTexture(GL_TEXTURE_RECTANGLE_ARB, xfbSource->texture);

			// We must call ApplyShader here even if no post proc is selected - it takes
			// care of disabling it in that case. It returns false in case of no post processing.
			if (applyShader)
			{
				glBegin(GL_QUADS);
					glTexCoord2f(sourceRc.left, sourceRc.bottom);  glMultiTexCoord2fARB(GL_TEXTURE1, 0, 0); glVertex2f(drawRc.left, drawRc.bottom);
					glTexCoord2f(sourceRc.left, sourceRc.top);     glMultiTexCoord2fARB(GL_TEXTURE1, 0, 1); glVertex2f(drawRc.left, drawRc.top);
					glTexCoord2f(sourceRc.right, sourceRc.top);    glMultiTexCoord2fARB(GL_TEXTURE1, 1, 1); glVertex2f(drawRc.right, drawRc.top);
					glTexCoord2f(sourceRc.right, sourceRc.bottom); glMultiTexCoord2fARB(GL_TEXTURE1, 1, 0); glVertex2f(drawRc.right, drawRc.bottom);
				glEnd();
				PixelShaderCache::DisableShader();
			}
			else
			{
				glBegin(GL_QUADS);
					glTexCoord2f(sourceRc.left, sourceRc.bottom);  glVertex2f(drawRc.left, drawRc.bottom);
					glTexCoord2f(sourceRc.left, sourceRc.top);	   glVertex2f(drawRc.left, drawRc.top);
					glTexCoord2f(sourceRc.right, sourceRc.top);	   glVertex2f(drawRc.right, drawRc.top);
					glTexCoord2f(sourceRc.right, sourceRc.bottom); glVertex2f(drawRc.right, drawRc.bottom);
				glEnd();
			}

			GL_REPORT_ERRORD();
		}
	}
	else
	{
		TargetRectangle targetRc = Renderer::ConvertEFBRectangle(Rc);
		GLuint read_texture = g_framebufferManager.ResolveAndGetRenderTarget(Rc);
		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, read_texture);
		if (applyShader)
		{
			glBegin(GL_QUADS);
				glTexCoord2f(targetRc.left, targetRc.bottom);  glMultiTexCoord2fARB(GL_TEXTURE1, 0, 0); glVertex2f(-1, -1);
				glTexCoord2f(targetRc.left, targetRc.top);     glMultiTexCoord2fARB(GL_TEXTURE1, 0, 1); glVertex2f(-1,  1);
				glTexCoord2f(targetRc.right, targetRc.top);    glMultiTexCoord2fARB(GL_TEXTURE1, 1, 1); glVertex2f( 1,  1);
				glTexCoord2f(targetRc.right, targetRc.bottom); glMultiTexCoord2fARB(GL_TEXTURE1, 1, 0); glVertex2f( 1, -1);
			glEnd();
			PixelShaderCache::DisableShader();
		}
		else
		{
			glBegin(GL_QUADS);
				glTexCoord2f(targetRc.left, targetRc.bottom);  glVertex2f(-1, -1);
				glTexCoord2f(targetRc.left, targetRc.top);	   glVertex2f(-1, 1);
				glTexCoord2f(targetRc.right, targetRc.top);	   glVertex2f( 1, 1);
				glTexCoord2f(targetRc.right, targetRc.bottom); glVertex2f( 1, -1);
			glEnd();
		}
		
		
	}
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
	TextureMngr::DisableStage(0);

	// Wireframe
	if (g_ActiveConfig.bWireFrame)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	// Save screenshot
	if (s_bScreenshot)
	{
		if (!s_tempScreenshotFramebuffer)
			glGenFramebuffersEXT(1, &s_tempScreenshotFramebuffer);

		glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_tempScreenshotFramebuffer);
		glFramebufferTexture2DEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, xfbSource->texture, 0);

		s_criticalScreenshot.Enter();
		// Save screenshot
		SaveRenderTarget(s_sScreenshotName.c_str(), xfbSource->sourceRc.GetWidth(), xfbSource->sourceRc.GetHeight());
		// Reset settings
		s_sScreenshotName = "";
		s_bScreenshot = false;
		s_criticalScreenshot.Leave();

		glFramebufferTexture2DEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
		glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, g_framebufferManager.GetEFBFramebuffer());
	}

	// Frame dumps are handled a little differently in Windows
#ifdef _WIN32
	if (g_ActiveConfig.bDumpFrames)
	{
		if (!s_tempScreenshotFramebuffer)
			glGenFramebuffersEXT(1, &s_tempScreenshotFramebuffer);

		glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_tempScreenshotFramebuffer);
		glFramebufferTexture2DEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, xfbSource->texture, 0);

		s_criticalScreenshot.Enter();
		int w = xfbSource->sourceRc.GetWidth();
		int h = xfbSource->sourceRc.GetHeight();

		u8 *data = (u8 *) malloc(3 * w * h);
		glPixelStorei(GL_PACK_ALIGNMENT, 1);
		glReadPixels(0, Renderer::GetTargetHeight() - h, w, h, GL_BGR, GL_UNSIGNED_BYTE, data);
		if (glGetError() == GL_NO_ERROR && w > 0 && h > 0)
		{
			if (!s_bLastFrameDumped)
			{
				s_bAVIDumping = AVIDump::Start(EmuWindow::GetParentWnd(), w, h);
				if (!s_bAVIDumping) 
					OSD::AddMessage("AVIDump Start failed", 2000);
				else
				{
					OSD::AddMessage(StringFromFormat(
						"Dumping Frames to \"%sframedump0.avi\" (%dx%d RGB24)", File::GetUserPath(D_DUMPFRAMES_IDX), w, h).c_str(), 2000);
				}
			}
			if (s_bAVIDumping)
				AVIDump::AddFrame((char *) data);

			s_bLastFrameDumped = true;
		}
		else
		{
			NOTICE_LOG(VIDEO, "Error reading framebuffer");
		}
		free(data);
		s_criticalScreenshot.Leave();

		glFramebufferTexture2DEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
		glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, g_framebufferManager.GetEFBFramebuffer());
	}
	else
	{
		if(s_bLastFrameDumped && s_bAVIDumping)
		{
			AVIDump::Stop();
			s_bAVIDumping = false;
			OSD::AddMessage("Stop dumping frames to AVI", 2000);
		}
		s_bLastFrameDumped = false;
	}
#else
	if (g_ActiveConfig.bDumpFrames) {
		s_criticalScreenshot.Enter();
		char movie_file_name[255];
		int w = OpenGL_GetBackbufferWidth();
		int h = OpenGL_GetBackbufferHeight();
		u8 *data = (u8 *) malloc(3 * w * h);
		glPixelStorei(GL_PACK_ALIGNMENT, 1);
		glReadPixels(0, Renderer::GetTargetHeight() - h, w, h, GL_RGB, GL_UNSIGNED_BYTE, data);
		if (glGetError() == GL_NO_ERROR) {
			if (!s_bLastFrameDumped) {
				sprintf(movie_file_name, "%sframedump.raw", File::GetUserPath(D_DUMPFRAMES_IDX));
				f_pFrameDump = fopen(movie_file_name, "wb");
				if (f_pFrameDump == NULL) {
					PanicAlert("Error opening framedump.raw for writing.");
				} else {
					char msg [255];
					sprintf(msg, "Dumping Frames to \"%s\" (%dx%d RGB24)", movie_file_name, w, h);
					OSD::AddMessage(msg, 2000);
				}
			}
			if (f_pFrameDump != NULL) {
				FlipImageData(data, w, h);
				fwrite(data, w * 3, h, f_pFrameDump);
				fflush(f_pFrameDump);
			}
			s_bLastFrameDumped = true;
		}
		free(data);
		s_criticalScreenshot.Leave();
	} else {
		if (s_bLastFrameDumped && f_pFrameDump != NULL) {
			fclose(f_pFrameDump);
			f_pFrameDump = NULL;
		}

		s_bLastFrameDumped = false;
	}
#endif

	OpenGL_Update(); // just updates the render window position and the backbuffer size
	bool xfbchanged = false;
	if(s_XFB_width != fbWidth || s_XFB_height != fbHeight)
	{
		xfbchanged = true;
		s_XFB_width = fbWidth;
		s_XFB_height = fbHeight;
		if(s_XFB_width < 1) s_XFB_width = MAX_XFB_WIDTH;
		if(s_XFB_width > MAX_XFB_WIDTH) s_XFB_width = MAX_XFB_WIDTH;
		if(s_XFB_height < 1) s_XFB_height = MAX_XFB_HEIGHT;
		if(s_XFB_height > MAX_XFB_HEIGHT) s_XFB_height = MAX_XFB_HEIGHT;

	}
	bool WindowResized = false;
	int W = (int)OpenGL_GetBackbufferWidth(), H = (int)OpenGL_GetBackbufferHeight();
	if (W != m_CustomWidth || H != m_CustomHeight)
	{
		WindowResized = true;
		m_CustomWidth = W;
		m_CustomHeight = H;
	}


	if( xfbchanged || WindowResized)
	{
		TargetRectangle dst_rect;
		ComputeDrawRectangle(m_CustomWidth, m_CustomHeight, false, &dst_rect);

		xScale = 1.0f;
		yScale = 1.0f;

		if(!g_ActiveConfig.bNativeResolution)
		{
			if (g_ActiveConfig.b2xResolution)
			{
				xScale = 2.0f;
				yScale = 2.0f;
			}
			else
			{
				xScale = (float)(dst_rect.right - dst_rect.left) / (float)s_XFB_width;
				yScale = (float)(dst_rect.bottom - dst_rect.top) / (float)s_XFB_height;
			}
		}
		
		
		EFBxScale = ceilf(xScale);
		EFByScale = ceilf(yScale);

		int m_newFrameBufferWidth  = EFB_WIDTH * EFBxScale;
		int m_newFrameBufferHeight = EFB_HEIGHT * EFByScale;
		if(m_newFrameBufferWidth != m_FrameBufferWidth || m_newFrameBufferHeight != m_FrameBufferHeight )
		{
			m_FrameBufferWidth  = m_newFrameBufferWidth;
			m_FrameBufferHeight = m_newFrameBufferHeight;

			g_framebufferManager.Shutdown();
			g_framebufferManager.Init(m_FrameBufferWidth, m_FrameBufferHeight, s_MSAASamples, s_MSAACoverageSamples);
			glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
		}
		
	}

	// Place messages on the picture, then copy it to the screen
	// ---------------------------------------------------------------------
	// Count FPS.
	// -------------
	static int fpscount = 0;
	static unsigned long lasttime;
	++fpscount;
	if (Common::Timer::GetTimeMs() - lasttime > 1000)
	{
		lasttime = Common::Timer::GetTimeMs();
		s_fps = fpscount - 1;
		fpscount = 0;
	}
	// ---------------------------------------------------------------------
	GL_REPORT_ERRORD();
	
	DrawDebugText();

	GL_REPORT_ERRORD();

	// Get the status of the Blend mode
	GLboolean blend_enabled = glIsEnabled(GL_BLEND);
	glDisable(GL_BLEND);
	OSD::DrawMessages();
	if (blend_enabled)
		glEnable(GL_BLEND);
	GL_REPORT_ERRORD();
#if defined(DVPROFILE)
	if (g_bWriteProfile) {
		//g_bWriteProfile = 0;
		static int framenum = 0;
		const int UPDATE_FRAMES = 8;
		if (++framenum >= UPDATE_FRAMES) {
			DVProfWrite("prof.txt", UPDATE_FRAMES);
			DVProfClear();
			framenum = 0;
		}
	}
#endif
	// Copy the rendered frame to the real window
	OpenGL_SwapBuffers();
	
	GL_REPORT_ERRORD();

	// Clear framebuffer
	glClearColor(0, 0, 0, 0);
	glClear(GL_COLOR_BUFFER_BIT);

	GL_REPORT_ERRORD();

	// Clean out old stuff from caches. It's not worth it to clean out the shader caches.
	DLCache::ProgressiveCleanup();
	TextureMngr::ProgressiveCleanup();

	frameCount++;

	// New frame
	stats.ResetFrame();

	// Render to the framebuffer.
	g_framebufferManager.SetFramebuffer(0);

	GL_REPORT_ERRORD();

	RestoreAPIState();

	GL_REPORT_ERRORD();
	g_Config.iSaveTargetId = 0;

	bool last_copy_efb_to_Texture = g_ActiveConfig.bCopyEFBToTexture;
	UpdateActiveConfig();
	if (last_copy_efb_to_Texture != g_ActiveConfig.bCopyEFBToTexture)
		TextureMngr::ClearRenderTargets();

	// For testing zbuffer targets.
	// Renderer::SetZBufferRender();
	// SaveTexture("tex.tga", GL_TEXTURE_RECTANGLE_ARB, s_FakeZTarget, GetTargetWidth(), GetTargetHeight());
	g_VideoInitialize.pCopiedToXFB(XFBWrited || g_ActiveConfig.bUseRealXFB);	
	XFBWrited = false;
	
}

// Create On-Screen-Messages
void Renderer::DrawDebugText()
{
	// Reset viewport for drawing text
	glViewport(0, 0, OpenGL_GetBackbufferWidth(), OpenGL_GetBackbufferHeight());
	// Draw various messages on the screen, like FPS, statistics, etc.
	char debugtext_buffer[8192];
	char *p = debugtext_buffer;
	p[0] = 0;

	if (g_ActiveConfig.bShowFPS)
		p+=sprintf(p, "FPS: %d\n", s_fps);

	if (g_ActiveConfig.bShowEFBCopyRegions)
	{
		// Store Line Size
		GLfloat lSize;
		glGetFloatv(GL_LINE_WIDTH, &lSize);

		// Set Line Size
		glLineWidth(3.0f);

		glBegin(GL_LINES);

		// Draw EFB copy regions rectangles
		for (std::vector<EFBRectangle>::const_iterator it = stats.efb_regions.begin(); it != stats.efb_regions.end(); ++it)
		{
			GLfloat halfWidth = EFB_WIDTH / 2.0f;
			GLfloat halfHeight = EFB_HEIGHT / 2.0f;
			GLfloat x =  (GLfloat) -1.0f + ((GLfloat)it->left / halfWidth);
			GLfloat y =  (GLfloat) 1.0f - ((GLfloat)it->top / halfHeight);
			GLfloat x2 = (GLfloat) -1.0f + ((GLfloat)it->right / halfWidth);
			GLfloat y2 = (GLfloat) 1.0f - ((GLfloat)it->bottom / halfHeight);

			// Draw shadow of rect
			glColor3f(0.0f, 0.0f, 0.0f);
			glVertex2f(x, y - 0.01);  glVertex2f(x2, y - 0.01);
			glVertex2f(x, y2 - 0.01); glVertex2f(x2, y2 - 0.01);
			glVertex2f(x + 0.005, y);  glVertex2f(x + 0.005, y2);
			glVertex2f(x2 + 0.005, y); glVertex2f(x2 + 0.005, y2);

			// Draw rect
			glColor3f(0.0f, 1.0f, 1.0f);
			glVertex2f(x, y);  glVertex2f(x2, y);
			glVertex2f(x, y2); glVertex2f(x2, y2);
			glVertex2f(x, y);  glVertex2f(x, y2);
			glVertex2f(x2, y); glVertex2f(x2, y2);
		}

		glEnd();

		// Restore Line Size
		glLineWidth(lSize);

		// Clear stored regions
		stats.efb_regions.clear();
	}

	if (g_ActiveConfig.bOverlayStats)
	{
		p = Statistics::ToString(p);
	}

	if (g_ActiveConfig.bOverlayProjStats)
	{
		p = Statistics::ToStringProj(p);
	}

	// Render a shadow, and then the text.
	if (p != debugtext_buffer)
	{
		Renderer::RenderText(debugtext_buffer, 21, 21, 0xDD000000);
		Renderer::RenderText(debugtext_buffer, 20, 20, 0xFF00FFFF);
	}

	// OSD Menu messages
	if (g_ActiveConfig.bOSDHotKey)
	{
		if (OSDChoice > 0)
		{
			OSDTime = Common::Timer::GetTimeMs() + 3000;
			OSDChoice = -OSDChoice;
		}
		if ((u32)OSDTime > Common::Timer::GetTimeMs())
		{
			std::string T1 = "", T2 = "";
			std::vector<std::string> T0;

			int W, H;
			W = OpenGL_GetBackbufferWidth();
			H = OpenGL_GetBackbufferHeight();

			std::string OSDM1 =
				g_ActiveConfig.bNativeResolution || g_ActiveConfig.b2xResolution ? 
				(g_ActiveConfig.bNativeResolution ? 
				StringFromFormat("%i x %i (native)", OSDInternalW, OSDInternalH)
				: StringFromFormat("%i x %i (2x)", OSDInternalW, OSDInternalH))
				: StringFromFormat("%i x %i (custom)", W, H);
			std::string OSDM21;
			switch(g_ActiveConfig.iAspectRatio)
			{
			case ASPECT_AUTO:
				OSDM21 = "Auto";
				break;
			case ASPECT_FORCE_16_9:
				OSDM21 = "16:9";
				break;
			case ASPECT_FORCE_4_3:
				OSDM21 = "4:3";
				break;
			case ASPECT_STRETCH:
				OSDM21 = "Stretch";
				break;
			}
			std::string OSDM22 =
				g_ActiveConfig.bCrop ? " (crop)" : "";
			std::string OSDM3 = g_ActiveConfig.bEFBCopyDisable ? "Disabled" :
				g_ActiveConfig.bCopyEFBToTexture ? "To Texture" : "To RAM";

			// If there is more text than this we will have a collission
			if (g_ActiveConfig.bShowFPS)
				{ T1 += "\n\n"; T2 += "\n\n"; }

			// The rows
			T0.push_back(StringFromFormat("3: Internal Resolution: %s\n", OSDM1.c_str()));
			T0.push_back(StringFromFormat("4: Aspect Ratio: %s%s\n", OSDM21.c_str(), OSDM22.c_str()));
			T0.push_back(StringFromFormat("5: Copy EFB: %s\n", OSDM3.c_str()));
			T0.push_back(StringFromFormat("6: Fog: %s\n", g_ActiveConfig.bDisableFog ? "Disabled" : "Enabled"));
			T0.push_back(StringFromFormat("7: Material Lighting: %s\n", g_ActiveConfig.bDisableLighting ? "Disabled" : "Enabled"));

			// The latest changed setting in yellow
			T1 += (OSDChoice == -1) ? T0.at(0) : "\n";
			T1 += (OSDChoice == -2) ? T0.at(1) : "\n";
			T1 += (OSDChoice == -3) ? T0.at(2) : "\n";
			T1 += (OSDChoice == -4) ? T0.at(3) : "\n";
			T1 += (OSDChoice == -5) ? T0.at(4) : "\n";

			// The other settings in cyan
			T2 += (OSDChoice != -1) ? T0.at(0) : "\n";
			T2 += (OSDChoice != -2) ? T0.at(1) : "\n";
			T2 += (OSDChoice != -3) ? T0.at(2) : "\n";
			T2 += (OSDChoice != -4) ? T0.at(3) : "\n";
			T2 += (OSDChoice != -5) ? T0.at(4) : "\n";

			// Render a shadow, and then the text
			Renderer::RenderText(T1.c_str(), 21, 21, 0xDD000000);
			Renderer::RenderText(T1.c_str(), 20, 20, 0xFFffff00);
			Renderer::RenderText(T2.c_str(), 21, 21, 0xDD000000);
			Renderer::RenderText(T2.c_str(), 20, 20, 0xFF00FFFF);
		}
	}
}
void Renderer::RenderText(const char* pstr, int left, int top, u32 color)
{
	int nBackbufferWidth = (int)OpenGL_GetBackbufferWidth();
	int nBackbufferHeight = (int)OpenGL_GetBackbufferHeight();
	glColor4f(((color>>16) & 0xff)/255.0f, ((color>> 8) & 0xff)/255.0f, 
			  ((color>> 0) & 0xff)/255.0f, ((color>>24) & 0xFF)/255.0f);
	s_pfont->printMultilineText(pstr, 
		left * 2.0f / (float)nBackbufferWidth - 1, 
		1 - top * 2.0f / (float)nBackbufferHeight, 
		0, nBackbufferWidth, nBackbufferHeight);
	GL_REPORT_ERRORD();
}

// Save screenshot
void Renderer::SetScreenshot(const char *filename)
{
	s_criticalScreenshot.Enter();
	s_sScreenshotName = filename;
	s_bScreenshot = true;
	s_criticalScreenshot.Leave();
}

#if defined(HAVE_WX) && HAVE_WX
THREAD_RETURN TakeScreenshot(void *pArgs)
{
	ScrStrct *threadStruct = (ScrStrct *)pArgs;
	
	// These will contain the final image size
	float FloatW = (float)threadStruct->W;
	float FloatH = (float)threadStruct->H;

	// Handle aspect ratio for the final ScrStrct to look exactly like what's on screen.
	if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH)
	{
		bool use16_9 = g_VideoInitialize.bAutoAspectIs16_9;
		
		// Check for force-settings and override.
		if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_16_9)
			use16_9 = true;
		else if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_4_3)
			use16_9 = false;
		
		float Ratio = (FloatW / FloatH) / (!use16_9 ? (4.0f / 3.0f) : (16.0f / 9.0f));
		
		// If ratio > 1 the picture is too wide and we have to limit the width.
		if (Ratio > 1)
			FloatW /= Ratio;
		// ratio == 1 or the image is too high, we have to limit the height.
		else
			FloatH *= Ratio;
		
		// This is a bit expensive on high resolutions
		threadStruct->img->Rescale((int)FloatW, (int)FloatH, wxIMAGE_QUALITY_HIGH);
	}

	// Save the screenshot and finally kill the wxImage object
	// This is really expensive when saving to PNG, but not at all when using BMP
	threadStruct->img->SaveFile(wxString::FromAscii(threadStruct->filename.c_str()), wxBITMAP_TYPE_PNG);
	threadStruct->img->Destroy();
	
	// Show success messages
	OSD::AddMessage(StringFromFormat("Saved %i x %i %s", (int)FloatW, (int)FloatH, threadStruct->filename.c_str()).c_str(), 2000);
	delete threadStruct;

	return 0;
}
#endif

bool Renderer::SaveRenderTarget(const char *filename, int W, int H, int YOffset)
{
	u8 *data = (u8 *)malloc(3 * W * H);
	glPixelStorei(GL_PACK_ALIGNMENT, 1);
	
	glReadPixels(0, Renderer::GetTargetHeight() - H + YOffset, W, H, GL_RGB, GL_UNSIGNED_BYTE, data);
	
	// Show failure message
	if (glGetError() != GL_NO_ERROR)
	{
		OSD::AddMessage("Error capturing or saving screenshot.", 2000);
		return false;
	}
	
	// Turn image upside down
	FlipImageData(data, W, H);
	
#if defined(HAVE_WX) && HAVE_WX
	// Create wxImage
	wxImage *a = new wxImage(W, H, data);
	
	if (scrshotThread)
	{
		delete scrshotThread;
		scrshotThread = NULL;
	}
	
	ScrStrct *threadStruct = new ScrStrct;
	threadStruct->filename = std::string(filename);
	threadStruct->img = a;
	threadStruct->H = H; threadStruct->W = W;
	
	scrshotThread = new Common::Thread(TakeScreenshot, threadStruct);
#ifdef _WIN32
	scrshotThread->SetPriority(THREAD_PRIORITY_BELOW_NORMAL);
#endif
	bool result = true;
	
	OSD::AddMessage("Saving Screenshot... ", 2000);
	
#else
	bool result = SaveTGA(filename, W, H, data);
	free(data);
#endif
	
	return result;
}


void Renderer::FlipImageData(u8 *data, int w, int h)
{
	// Flip image upside down. Damn OpenGL.
	for (int y = 0; y < h / 2; y++)
	{
		for(int x = 0; x < w; x++)
		{
			std::swap(data[(y * w + x) * 3],     data[((h - 1 - y) * w + x) * 3]);
			std::swap(data[(y * w + x) * 3 + 1], data[((h - 1 - y) * w + x) * 3 + 1]);
			std::swap(data[(y * w + x) * 3 + 2], data[((h - 1 - y) * w + x) * 3 + 2]);
		}
	}
}

// Called from VertexShaderManager
void UpdateViewport()
{
	// reversed gxsetviewport(xorig, yorig, width, height, nearz, farz)
	// [0] = width/2
	// [1] = height/2
	// [2] = 16777215 * (farz - nearz)
	// [3] = xorig + width/2 + 342
	// [4] = yorig + height/2 + 342
	// [5] = 16777215 * farz
	int scissorXOff = bpmem.scissorOffset.x * 2;   // 342
	int scissorYOff = bpmem.scissorOffset.y * 2;   // 342
	
	// Stretch picture with increased internal resolution
	int GLx = (int)ceil((xfregs.rawViewport[3] - xfregs.rawViewport[0] - scissorXOff) * EFBxScale);
	int GLy = (int)ceil((float)((int)(EFB_HEIGHT - xfregs.rawViewport[4] + xfregs.rawViewport[1] + scissorYOff)) * EFByScale);
	int GLWidth = (int)ceil((float)(2 * xfregs.rawViewport[0]) * EFBxScale);
	int GLHeight = (int)ceil((float)(-2 * xfregs.rawViewport[1]) * EFByScale);
	double GLNear = (xfregs.rawViewport[5] - xfregs.rawViewport[2]) / 16777216.0f;
	double GLFar = xfregs.rawViewport[5] / 16777216.0f;
	if(GLWidth < 0)
	{
		GLx += GLWidth;
		GLWidth*=-1;
	}
	if(GLHeight < 0)
	{
		GLy += GLHeight;
		GLHeight *= -1;
	}
	// Update the view port
	glViewport(GLx, GLy, GLWidth, GLHeight);
	glDepthRange(GLNear, GLFar);
}

void Renderer::SetGenerationMode()
{
	// none, ccw, cw, ccw
	if (bpmem.genMode.cullmode > 0)
	{
		glEnable(GL_CULL_FACE);
		glFrontFace(bpmem.genMode.cullmode == 2 ? GL_CCW : GL_CW);
	}
	else
		glDisable(GL_CULL_FACE);
}

void Renderer::SetDepthMode()
{
	if (bpmem.zmode.testenable)
	{
		glEnable(GL_DEPTH_TEST);
		glDepthMask(bpmem.zmode.updateenable ? GL_TRUE : GL_FALSE);
		glDepthFunc(glCmpFuncs[bpmem.zmode.func]);
	}
	else
	{
		// if the test is disabled write is disabled too
		glDisable(GL_DEPTH_TEST);
		glDepthMask(GL_FALSE);
	}
}

void Renderer::SetLogicOpMode()
{
	if (bpmem.blendmode.logicopenable && bpmem.blendmode.logicmode != 3)
	{
		glEnable(GL_COLOR_LOGIC_OP);
		glLogicOp(glLogicOpCodes[bpmem.blendmode.logicmode]);
	}
	else
		glDisable(GL_COLOR_LOGIC_OP);
}

void Renderer::SetDitherMode()
{
	if (bpmem.blendmode.dither)
		glEnable(GL_DITHER);
	else
		glDisable(GL_DITHER);
}


void Renderer::SetLineWidth()
{
	float fratio = xfregs.rawViewport[0] != 0 ? ((float)Renderer::GetTargetWidth() / EFB_WIDTH) : 1.0f;
	if (bpmem.lineptwidth.linesize > 0)
		glLineWidth((float)bpmem.lineptwidth.linesize * fratio / 6.0f); // scale by ratio of widths
	if (bpmem.lineptwidth.pointsize > 0)
		glPointSize((float)bpmem.lineptwidth.pointsize * fratio / 6.0f);
}

void Renderer::SetSamplerState(int stage,int texindex)
{
	// TODO
}

void Renderer::SetInterlacingMode()
{
	// TODO
}