forked from dolphin-emu/dolphin
		
	
		
			
				
	
	
		
			391 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			391 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2013 Dolphin Emulator Project
 | |
| // Licensed under GPLv2
 | |
| // Refer to the license.txt file included.
 | |
| 
 | |
| #include "Common/Common.h"
 | |
| #include "Common/MemoryUtil.h"
 | |
| 
 | |
| #include "VideoBackends/OGL/GLInterfaceBase.h"
 | |
| #include "VideoBackends/Software/BPMemLoader.h"
 | |
| #include "VideoBackends/Software/DebugUtil.h"
 | |
| #include "VideoBackends/Software/HwRasterizer.h"
 | |
| #include "VideoBackends/Software/NativeVertexFormat.h"
 | |
| 
 | |
| #include "VideoCommon/VideoCommon.h"
 | |
| 
 | |
| #define TEMP_SIZE (1024*1024*4)
 | |
| 
 | |
| namespace HwRasterizer
 | |
| {
 | |
| 	static float efbHalfWidth;
 | |
| 	static float efbHalfHeight;
 | |
| 	static bool hasTexture;
 | |
| 
 | |
| 	static u8 *temp;
 | |
| 
 | |
| 	// Programs
 | |
| 	static GLuint colProg, texProg, clearProg;
 | |
| 
 | |
| 	// Texture type
 | |
| 	static GLenum texType;
 | |
| 
 | |
| 	// Color
 | |
| 	static GLint col_apos = -1, col_atex = -1;
 | |
| 	// Tex
 | |
| 	static GLint tex_apos = -1, tex_atex = -1, tex_utex = -1;
 | |
| 	// Clear shader
 | |
| 	static GLint clear_apos = -1, clear_ucol = -1;
 | |
| 
 | |
| 	static void CreateShaders()
 | |
| 	{
 | |
| 		// Color Vertices
 | |
| 		static const char *fragcolText =
 | |
| 			"#ifdef GL_ES\n"
 | |
| 			"precision highp float;\n"
 | |
| 			"#endif\n"
 | |
| 			"varying vec4 TexCoordOut;\n"
 | |
| 			"void main() {\n"
 | |
| 			"	gl_FragColor = TexCoordOut;\n"
 | |
| 			"}\n";
 | |
| 		// Texture Vertices
 | |
| 		static const char *fragtexText =
 | |
| 			"#ifdef GL_ES\n"
 | |
| 			"precision highp float;\n"
 | |
| 			"#define texture2DRect texture2D\n"
 | |
| 			"#define sampler2DRect sampler2D\n"
 | |
| 			"#endif\n"
 | |
| 			"varying vec4 TexCoordOut;\n"
 | |
| 			"uniform sampler2DRect Texture;\n"
 | |
| 			"void main() {\n"
 | |
| 			"	gl_FragColor = texture2DRect(Texture, TexCoordOut.xy);\n"
 | |
| 			"}\n";
 | |
| 		// Clear shader
 | |
| 		static const char *fragclearText =
 | |
| 			"#ifdef GL_ES\n"
 | |
| 			"precision highp float;\n"
 | |
| 			"#endif\n"
 | |
| 			"uniform vec4 Color;\n"
 | |
| 			"void main() {\n"
 | |
| 			"	gl_FragColor = Color;\n"
 | |
| 			"}\n";
 | |
| 		// Generic passthrough vertice shaders
 | |
| 		static const char *vertShaderText =
 | |
| 			"attribute vec4 pos;\n"
 | |
| 			"attribute vec4 TexCoordIn;\n "
 | |
| 			"varying vec4 TexCoordOut;\n "
 | |
| 			"void main() {\n"
 | |
| 			"	gl_Position = pos;\n"
 | |
| 			"	TexCoordOut = TexCoordIn;\n"
 | |
| 			"}\n";
 | |
| 		static const char *vertclearText =
 | |
| 			"attribute vec4 pos;\n"
 | |
| 			"void main() {\n"
 | |
| 			"	gl_Position = pos;\n"
 | |
| 			"}\n";
 | |
| 
 | |
| 		// Color Program
 | |
| 		colProg = OpenGL_CompileProgram(vertShaderText, fragcolText);
 | |
| 
 | |
| 		// Texture Program
 | |
| 		texProg = OpenGL_CompileProgram(vertShaderText, fragtexText);
 | |
| 
 | |
| 		// Clear Program
 | |
| 		clearProg = OpenGL_CompileProgram(vertclearText, fragclearText);
 | |
| 
 | |
| 		// Color attributes
 | |
| 		col_apos = glGetAttribLocation(colProg, "pos");
 | |
| 		col_atex = glGetAttribLocation(colProg, "TexCoordIn");
 | |
| 		// Texture attributes
 | |
| 		tex_apos = glGetAttribLocation(texProg, "pos");
 | |
| 		tex_atex = glGetAttribLocation(texProg, "TexCoordIn");
 | |
| 		tex_utex = glGetUniformLocation(texProg, "Texture");
 | |
| 		// Clear attributes
 | |
| 		clear_apos = glGetAttribLocation(clearProg, "pos");
 | |
| 		clear_ucol = glGetUniformLocation(clearProg, "Color");
 | |
| 	}
 | |
| 
 | |
| 	void Init()
 | |
| 	{
 | |
| 		efbHalfWidth = EFB_WIDTH / 2.0f;
 | |
| 		efbHalfHeight = 480 / 2.0f;
 | |
| 
 | |
| 		temp = (u8*)AllocateMemoryPages(TEMP_SIZE);
 | |
| 	}
 | |
| 	void Shutdown()
 | |
| 	{
 | |
| 		glDeleteProgram(colProg);
 | |
| 		glDeleteProgram(texProg);
 | |
| 		glDeleteProgram(clearProg);
 | |
| 	}
 | |
| 	void Prepare()
 | |
| 	{
 | |
| 		//legacy multitexturing: select texture channel only.
 | |
| 		glActiveTexture(GL_TEXTURE0);
 | |
| 		glPixelStorei(GL_UNPACK_ALIGNMENT, 4);  // 4-byte pixel alignment
 | |
| 		if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
 | |
| 		{
 | |
| 			glShadeModel(GL_SMOOTH);
 | |
| 			glDisable(GL_BLEND);
 | |
| 			glClearDepth(1.0f);
 | |
| 			glEnable(GL_SCISSOR_TEST);
 | |
| 			glDisable(GL_LIGHTING);
 | |
| 			glMatrixMode(GL_PROJECTION);
 | |
| 			glLoadIdentity();
 | |
| 			glMatrixMode(GL_MODELVIEW);
 | |
| 			glLoadIdentity();
 | |
| 
 | |
| 			glClientActiveTexture(GL_TEXTURE0);
 | |
| 			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 | |
| 			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 | |
| 			glEnable(GL_TEXTURE_RECTANGLE_ARB);
 | |
| 			glStencilFunc(GL_ALWAYS, 0, 0);
 | |
| 			glDisable(GL_STENCIL_TEST);
 | |
| 		}
 | |
| 		// used by hw rasterizer if it enables blending and depth test
 | |
| 		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 | |
| 		glDepthFunc(GL_LEQUAL);
 | |
| 
 | |
| 		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 | |
| 
 | |
| 		CreateShaders();
 | |
| 		if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
 | |
| 			texType = GL_TEXTURE_RECTANGLE;
 | |
| 		else
 | |
| 			texType = GL_TEXTURE_2D;
 | |
| 
 | |
| 		GL_REPORT_ERRORD();
 | |
| 	}
 | |
| 	static float width, height;
 | |
| 	static void LoadTexture()
 | |
| 	{
 | |
| 		FourTexUnits &texUnit = bpmem.tex[0];
 | |
| 		u32 imageAddr = texUnit.texImage3[0].image_base;
 | |
| 		// Texture Rectangle uses pixel coordinates
 | |
| 		// While GLES uses texture coordinates
 | |
| 		if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
 | |
| 		{
 | |
| 			width = (float)texUnit.texImage0[0].width;
 | |
| 			height = (float)texUnit.texImage0[0].height;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			width = 1;
 | |
| 			height = 1;
 | |
| 		}
 | |
| 		TexCacheEntry &cacheEntry = textures[imageAddr];
 | |
| 		cacheEntry.Update();
 | |
| 
 | |
| 		glBindTexture(texType, cacheEntry.texture);
 | |
| 		glTexParameteri(texType, GL_TEXTURE_MAG_FILTER, texUnit.texMode0[0].mag_filter ? GL_LINEAR : GL_NEAREST);
 | |
| 		glTexParameteri(texType, GL_TEXTURE_MIN_FILTER, (texUnit.texMode0[0].min_filter >= 4) ? GL_LINEAR : GL_NEAREST);
 | |
| 		GL_REPORT_ERRORD();
 | |
| 	}
 | |
| 
 | |
| 	void BeginTriangles()
 | |
| 	{
 | |
| 		// disabling depth test sometimes allows more things to be visible
 | |
| 		glEnable(GL_DEPTH_TEST);
 | |
| 		glEnable(GL_BLEND);
 | |
| 
 | |
| 		hasTexture = bpmem.tevorders[0].enable0;
 | |
| 
 | |
| 		if (hasTexture)
 | |
| 			LoadTexture();
 | |
| 	}
 | |
| 
 | |
| 	void EndTriangles()
 | |
| 	{
 | |
| 		glBindTexture(texType, 0);
 | |
| 		glDisable(GL_DEPTH_TEST);
 | |
| 		glDisable(GL_BLEND);
 | |
| 	}
 | |
| 
 | |
| 	static void DrawColorVertex(OutputVertexData *v0, OutputVertexData *v1, OutputVertexData *v2)
 | |
| 	{
 | |
| 		float x0 = (v0->screenPosition.x / efbHalfWidth) - 1.0f;
 | |
| 		float y0 = 1.0f - (v0->screenPosition.y / efbHalfHeight);
 | |
| 		float z0 = v0->screenPosition.z / (float)0x00ffffff;
 | |
| 
 | |
| 		float x1 = (v1->screenPosition.x / efbHalfWidth) - 1.0f;
 | |
| 		float y1 = 1.0f - (v1->screenPosition.y / efbHalfHeight);
 | |
| 		float z1 = v1->screenPosition.z / (float)0x00ffffff;
 | |
| 
 | |
| 		float x2 = (v2->screenPosition.x / efbHalfWidth) - 1.0f;
 | |
| 		float y2 = 1.0f - (v2->screenPosition.y / efbHalfHeight);
 | |
| 		float z2 = v2->screenPosition.z / (float)0x00ffffff;
 | |
| 
 | |
| 		float r0 = v0->color[0][OutputVertexData::RED_C] / 255.0f;
 | |
| 		float g0 = v0->color[0][OutputVertexData::GRN_C] / 255.0f;
 | |
| 		float b0 = v0->color[0][OutputVertexData::BLU_C] / 255.0f;
 | |
| 
 | |
| 		float r1 = v1->color[0][OutputVertexData::RED_C] / 255.0f;
 | |
| 		float g1 = v1->color[0][OutputVertexData::GRN_C] / 255.0f;
 | |
| 		float b1 = v1->color[0][OutputVertexData::BLU_C] / 255.0f;
 | |
| 
 | |
| 		float r2 = v2->color[0][OutputVertexData::RED_C] / 255.0f;
 | |
| 		float g2 = v2->color[0][OutputVertexData::GRN_C] / 255.0f;
 | |
| 		float b2 = v2->color[0][OutputVertexData::BLU_C] / 255.0f;
 | |
| 
 | |
| 		static const GLfloat verts[3][3] = {
 | |
| 			{ x0, y0, z0 },
 | |
| 			{ x1, y1, z1 },
 | |
| 			{ x2, y2, z2 }
 | |
| 		};
 | |
| 		static const GLfloat col[3][4] = {
 | |
| 			{ r0, g0, b0, 1.0f },
 | |
| 			{ r1, g1, b1, 1.0f },
 | |
| 			{ r2, g2, b2, 1.0f }
 | |
| 		};
 | |
| 		{
 | |
| 			glUseProgram(colProg);
 | |
| 			glEnableVertexAttribArray(col_apos);
 | |
| 			glEnableVertexAttribArray(col_atex);
 | |
| 
 | |
| 			glVertexAttribPointer(col_apos, 3, GL_FLOAT, GL_FALSE, 0, verts);
 | |
| 			glVertexAttribPointer(col_atex, 4, GL_FLOAT, GL_FALSE, 0, col);
 | |
| 				glDrawArrays(GL_TRIANGLES, 0, 3);
 | |
| 			glDisableVertexAttribArray(col_atex);
 | |
| 			glDisableVertexAttribArray(col_apos);
 | |
| 		}
 | |
| 		GL_REPORT_ERRORD();
 | |
| 	}
 | |
| 
 | |
| 	static void DrawTextureVertex(OutputVertexData *v0, OutputVertexData *v1, OutputVertexData *v2)
 | |
| 	{
 | |
| 		float x0 = (v0->screenPosition.x / efbHalfWidth) - 1.0f;
 | |
| 		float y0 = 1.0f - (v0->screenPosition.y / efbHalfHeight);
 | |
| 		float z0 = v0->screenPosition.z;
 | |
| 
 | |
| 		float x1 = (v1->screenPosition.x / efbHalfWidth) - 1.0f;
 | |
| 		float y1 = 1.0f - (v1->screenPosition.y / efbHalfHeight);
 | |
| 		float z1 = v1->screenPosition.z;
 | |
| 
 | |
| 		float x2 = (v2->screenPosition.x / efbHalfWidth) - 1.0f;
 | |
| 		float y2 = 1.0f - (v2->screenPosition.y / efbHalfHeight);
 | |
| 		float z2 = v2->screenPosition.z;
 | |
| 
 | |
| 		float s0 = v0->texCoords[0].x / width;
 | |
| 		float t0 = v0->texCoords[0].y / height;
 | |
| 
 | |
| 		float s1 = v1->texCoords[0].x / width;
 | |
| 		float t1 = v1->texCoords[0].y / height;
 | |
| 
 | |
| 		float s2 = v2->texCoords[0].x / width;
 | |
| 		float t2 = v2->texCoords[0].y / height;
 | |
| 
 | |
| 		static const GLfloat verts[3][3] = {
 | |
| 			{ x0, y0, z0 },
 | |
| 			{ x1, y1, z1 },
 | |
| 			{ x2, y2, z2 }
 | |
| 		};
 | |
| 		static const GLfloat tex[3][2] = {
 | |
| 			{ s0, t0 },
 | |
| 			{ s1, t1 },
 | |
| 			{ s2, t2 }
 | |
| 		};
 | |
| 		{
 | |
| 			glUseProgram(texProg);
 | |
| 			glEnableVertexAttribArray(tex_apos);
 | |
| 			glEnableVertexAttribArray(tex_atex);
 | |
| 
 | |
| 			glVertexAttribPointer(tex_apos, 3, GL_FLOAT, GL_FALSE, 0, verts);
 | |
| 			glVertexAttribPointer(tex_atex, 2, GL_FLOAT, GL_FALSE, 0, tex);
 | |
| 				glUniform1i(tex_utex, 0);
 | |
| 				glDrawArrays(GL_TRIANGLES, 0, 3);
 | |
| 			glDisableVertexAttribArray(tex_atex);
 | |
| 			glDisableVertexAttribArray(tex_apos);
 | |
| 		}
 | |
| 		GL_REPORT_ERRORD();
 | |
| 	}
 | |
| 
 | |
| 	void DrawTriangleFrontFace(OutputVertexData *v0, OutputVertexData *v1, OutputVertexData *v2)
 | |
| 	{
 | |
| 		if (hasTexture)
 | |
| 			DrawTextureVertex(v0, v1, v2);
 | |
| 		else
 | |
| 			DrawColorVertex(v0, v1, v2);
 | |
| 	}
 | |
| 
 | |
| 	void Clear()
 | |
| 	{
 | |
| 		u8 r = (bpmem.clearcolorAR & 0x00ff);
 | |
| 		u8 g = (bpmem.clearcolorGB & 0xff00) >> 8;
 | |
| 		u8 b = (bpmem.clearcolorGB & 0x00ff);
 | |
| 		u8 a = (bpmem.clearcolorAR & 0xff00) >> 8;
 | |
| 
 | |
| 		GLfloat left   = (GLfloat)bpmem.copyTexSrcXY.x / efbHalfWidth - 1.0f;
 | |
| 		GLfloat top    = 1.0f - (GLfloat)bpmem.copyTexSrcXY.y / efbHalfHeight;
 | |
| 		GLfloat right  = (GLfloat)(left + bpmem.copyTexSrcWH.x + 1) / efbHalfWidth - 1.0f;
 | |
| 		GLfloat bottom = 1.0f - (GLfloat)(top + bpmem.copyTexSrcWH.y + 1) / efbHalfHeight;
 | |
| 		GLfloat depth = (GLfloat)bpmem.clearZValue / (GLfloat)0x00ffffff;
 | |
| 		static const GLfloat verts[4][3] = {
 | |
| 			{ left, top, depth },
 | |
| 			{ right, top, depth },
 | |
| 			{ right, bottom, depth },
 | |
| 			{ left, bottom, depth }
 | |
| 		};
 | |
| 		{
 | |
| 			glUseProgram(clearProg);
 | |
| 			glVertexAttribPointer(clear_apos, 3, GL_FLOAT, GL_FALSE, 0, verts);
 | |
| 			glUniform4f(clear_ucol, r, g, b, a);
 | |
| 			glEnableVertexAttribArray(col_apos);
 | |
| 				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 | |
| 			glDisableVertexAttribArray(col_apos);
 | |
| 		}
 | |
| 		GL_REPORT_ERRORD();
 | |
| 	}
 | |
| 
 | |
| 	TexCacheEntry::TexCacheEntry()
 | |
| 	{
 | |
| 		Create();
 | |
| 	}
 | |
| 
 | |
| 	void TexCacheEntry::Create()
 | |
| 	{
 | |
| 		FourTexUnits &texUnit = bpmem.tex[0];
 | |
| 
 | |
| 		texImage0.hex = texUnit.texImage0[0].hex;
 | |
| 		texImage1.hex = texUnit.texImage1[0].hex;
 | |
| 		texImage2.hex = texUnit.texImage2[0].hex;
 | |
| 		texImage3.hex = texUnit.texImage3[0].hex;
 | |
| 		texTlut.hex = texUnit.texTlut[0].hex;
 | |
| 
 | |
| 		int image_width = texImage0.width;
 | |
| 		int image_height = texImage0.height;
 | |
| 
 | |
| 		DebugUtil::GetTextureRGBA(temp, 0, 0, image_width, image_height);
 | |
| 
 | |
| 		glGenTextures(1, (GLuint *)&texture);
 | |
| 		glBindTexture(texType, texture);
 | |
| 		glTexImage2D(texType, 0, GL_RGBA, (GLsizei)image_width, (GLsizei)image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp);
 | |
| 
 | |
| 		GL_REPORT_ERRORD();
 | |
| 	}
 | |
| 
 | |
| 	void TexCacheEntry::Destroy()
 | |
| 	{
 | |
| 		if (texture == 0)
 | |
| 			return;
 | |
| 
 | |
| 		glDeleteTextures(1, &texture);
 | |
| 		texture = 0;
 | |
| 	}
 | |
| 
 | |
| 	void TexCacheEntry::Update()
 | |
| 	{
 | |
| 		FourTexUnits &texUnit = bpmem.tex[0];
 | |
| 
 | |
| 		// extra checks cause textures to be reloaded much more
 | |
| 		if (texUnit.texImage0[0].hex != texImage0.hex ||
 | |
| 		//	texUnit.texImage1[0].hex != texImage1.hex ||
 | |
| 		//	texUnit.texImage2[0].hex != texImage2.hex ||
 | |
| 			texUnit.texImage3[0].hex != texImage3.hex ||
 | |
| 			texUnit.texTlut[0].hex   != texTlut.hex)
 | |
| 		{
 | |
| 			Destroy();
 | |
| 			Create();
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 |