From d5bb6fd79e6e66ddcf10b27e7f5e21e604f31e9e Mon Sep 17 00:00:00 2001
From: rogerman <rogerman@users.sf.net>
Date: Mon, 4 May 2015 18:30:09 +0000
Subject: [PATCH] =?UTF-8?q?Render3D:=20-=20In=20the=20OpenGL=20renderer,?=
 =?UTF-8?q?=20do=20better=20handling=20of=20the=20geometry=20index=20buffe?=
 =?UTF-8?q?r,=20and=20also=20load=20its=20data=20in=20OpenGLRenderer::Begi?=
 =?UTF-8?q?nRender().=20-=20In=20the=20OpenGL=20renderer,=20remove=20a=20b?=
 =?UTF-8?q?unch=20of=20extraneous=20binds.=20-=20Fix=20bug=20in=20SoftRast?=
 =?UTF-8?q?erizer=20where=20clear-image=20depth=20wasn=E2=80=99t=20being?=
 =?UTF-8?q?=20written=20correctly.=20(Regression=20from=20r5176.)=20-=20Re?=
 =?UTF-8?q?duce=20the=20buffer=20sizes=20in=20the=20core=203D=20engine.=20?=
 =?UTF-8?q?-=20Do=20even=20more=20refactoring.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 desmume/src/OGLRender.cpp     | 662 +++++++++++++++++-----------------
 desmume/src/OGLRender.h       |  29 +-
 desmume/src/OGLRender_3_2.cpp |  95 +++--
 desmume/src/OGLRender_3_2.h   |   3 +-
 desmume/src/gfx3d.h           |   8 +-
 desmume/src/render3D.cpp      |   6 +-
 6 files changed, 407 insertions(+), 396 deletions(-)

diff --git a/desmume/src/OGLRender.cpp b/desmume/src/OGLRender.cpp
index 33d4d0fb2..54cf530c6 100644
--- a/desmume/src/OGLRender.cpp
+++ b/desmume/src/OGLRender.cpp
@@ -963,6 +963,9 @@ OpenGLRenderer_1_2::~OpenGLRenderer_1_2()
 	delete[] ref->color4fBuffer;
 	ref->color4fBuffer = NULL;
 	
+	delete[] ref->vertIndexBuffer;
+	ref->vertIndexBuffer = NULL;
+	
 	DestroyGeometryProgram();
 	DestroyPostprocessingPrograms();
 	DestroyVAOs();
@@ -1014,19 +1017,20 @@ Render3DError OpenGLRenderer_1_2::InitExtensions()
 		if (error == OGLERROR_NOERR)
 		{
 			error = this->InitGeometryProgram(vertexShaderProgram, fragmentShaderProgram);
-			if (error != OGLERROR_NOERR)
+			if (error == OGLERROR_NOERR)
 			{
-				this->isShaderSupported = false;
+				std::string edgeMarkVtxShaderString = std::string(EdgeMarkVtxShader_100);
+				std::string edgeMarkFragShaderString = std::string(EdgeMarkFragShader_100);
+				std::string fogVtxShaderString = std::string(FogVtxShader_100);
+				std::string fogFragShaderString = std::string(FogFragShader_100);
+				error = this->InitPostprocessingPrograms(edgeMarkVtxShaderString, edgeMarkFragShaderString, fogVtxShaderString, fogFragShaderString);
+				if (error != OGLERROR_NOERR)
+				{
+					INFO("OpenGL: Edge mark and fog require OpenGL v2.0 or later. These features will be disabled.\n");
+				}
 			}
-			
-			std::string edgeMarkVtxShaderString = std::string(EdgeMarkVtxShader_100);
-			std::string edgeMarkFragShaderString = std::string(EdgeMarkFragShader_100);
-			std::string fogVtxShaderString = std::string(FogVtxShader_100);
-			std::string fogFragShaderString = std::string(FogFragShader_100);
-			error = this->InitPostprocessingPrograms(edgeMarkVtxShaderString, edgeMarkFragShaderString, fogVtxShaderString, fogFragShaderString);
-			if (error != OGLERROR_NOERR)
+			else
 			{
-				this->DestroyGeometryProgram();
 				this->isShaderSupported = false;
 			}
 		}
@@ -1046,7 +1050,7 @@ Render3DError OpenGLRenderer_1_2::InitExtensions()
 		this->CreateVBOs();
 	}
 	
-	this->isPBOSupported	= this->IsExtensionPresent(&oglExtensionSet, "GL_ARB_vertex_buffer_object") &&
+	this->isPBOSupported	= this->isVBOSupported &&
 							 (this->IsExtensionPresent(&oglExtensionSet, "GL_ARB_pixel_buffer_object") ||
 							  this->IsExtensionPresent(&oglExtensionSet, "GL_EXT_pixel_buffer_object"));
 	if (this->isPBOSupported)
@@ -1648,7 +1652,11 @@ Render3DError OpenGLRenderer_1_2::InitFinalRenderStates(const std::set<std::stri
 	// because OpenGL needs 4-colors per vertex to support translucency. (The DS
 	// uses 3-colors per vertex, and adds alpha through the poly, so we can't
 	// simply reference the colors+alpha from just the vertices by themselves.)
-	OGLRef.color4fBuffer = this->isShaderSupported ? NULL : new GLfloat[VERTLIST_SIZE * 4];
+	OGLRef.color4fBuffer = (this->isShaderSupported) ? NULL : new GLfloat[VERTLIST_SIZE * 4];
+	
+	// If VBOs aren't supported, then we need to create the index buffer on the
+	// client side so that we have a buffer to update.
+	OGLRef.vertIndexBuffer = (this->isVBOSupported) ? NULL : new GLushort[OGLRENDER_VERT_INDEX_BUFFER_COUNT];
 	
 	return OGLERROR_NOERR;
 }
@@ -1731,34 +1739,26 @@ Render3DError OpenGLRenderer_1_2::DestroyToonTable()
 	return OGLERROR_NOERR;
 }
 
-Render3DError OpenGLRenderer_1_2::UploadToonTable(const u16 *toonTableBuffer)
-{
-	glActiveTextureARB(GL_TEXTURE0_ARB + OGLTextureUnitID_ToonTable);
-	glBindTexture(GL_TEXTURE_1D, this->ref->texToonTableID);
-	glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 32, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, toonTableBuffer);
-	glActiveTextureARB(GL_TEXTURE0_ARB);
-	
-	return OGLERROR_NOERR;
-}
-
 Render3DError OpenGLRenderer_1_2::UploadClearImage(const u16 *__restrict colorBuffer, const u32 *__restrict depthBuffer, const bool *__restrict fogBuffer, const u8 *__restrict polyIDBuffer)
 {
 	OGLRenderRef &OGLRef = *this->ref;
 	
 	static GLuint depth[GFX3D_FRAMEBUFFER_WIDTH * GFX3D_FRAMEBUFFER_HEIGHT];
+	static GLuint depthStencil[GFX3D_FRAMEBUFFER_WIDTH * GFX3D_FRAMEBUFFER_HEIGHT];
 	static GLuint fogAttributes[GFX3D_FRAMEBUFFER_WIDTH * GFX3D_FRAMEBUFFER_HEIGHT];
 	static GLuint polyID[GFX3D_FRAMEBUFFER_WIDTH * GFX3D_FRAMEBUFFER_HEIGHT];
 	
 	for (size_t i = 0; i < GFX3D_FRAMEBUFFER_WIDTH * GFX3D_FRAMEBUFFER_HEIGHT; i++)
 	{
 		depth[i] = depthBuffer[i] | 0xFF000000;
+		depthStencil[i] = depthBuffer[i] << 8;
 		fogAttributes[i] = (fogBuffer[i]) ? 0xFF0000FF : 0xFF000000;
 		polyID[i] = (GLuint)polyIDBuffer[i] | 0xFF000000;
 	}
 	
 	glActiveTextureARB(GL_TEXTURE0_ARB + OGLTextureUnitID_GColor);
 	glBindTexture(GL_TEXTURE_2D, OGLRef.texGDepthStencilID);
-	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, depthBuffer);
+	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, depthStencil);
 	glBindTexture(GL_TEXTURE_2D, OGLRef.texGColorID);
 	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, colorBuffer);
 	
@@ -1815,95 +1815,13 @@ Render3DError OpenGLRenderer_1_2::ExpandFreeTextures()
 	return OGLERROR_NOERR;
 }
 
-Render3DError OpenGLRenderer_1_2::SetupVertices(const VERTLIST *vertList, const POLYLIST *polyList, const INDEXLIST *indexList, GLushort *outIndexBuffer, size_t *outIndexCount)
-{
-	OGLRenderRef &OGLRef = *this->ref;
-	const size_t polyCount = polyList->count;
-	size_t vertIndexCount = 0;
-	
-	for(size_t i = 0; i < polyCount; i++)
-	{
-		const POLY *poly = &polyList->list[indexList->list[i]];
-		const size_t polyType = poly->type;
-		
-		if (this->isShaderSupported)
-		{
-			for(size_t j = 0; j < polyType; j++)
-			{
-				const GLushort vertIndex = poly->vertIndexes[j];
-				
-				// While we're looping through our vertices, add each vertex index to
-				// a buffer. For GFX3D_QUADS and GFX3D_QUAD_STRIP, we also add additional
-				// vertices here to convert them to GL_TRIANGLES, which are much easier
-				// to work with and won't be deprecated in future OpenGL versions.
-				outIndexBuffer[vertIndexCount++] = vertIndex;
-				if (poly->vtxFormat == GFX3D_QUADS || poly->vtxFormat == GFX3D_QUAD_STRIP)
-				{
-					if (j == 2)
-					{
-						outIndexBuffer[vertIndexCount++] = vertIndex;
-					}
-					else if (j == 3)
-					{
-						outIndexBuffer[vertIndexCount++] = poly->vertIndexes[0];
-					}
-				}
-			}
-		}
-		else
-		{
-			const GLfloat thePolyAlpha = (!poly->isWireframe() && poly->isTranslucent()) ? divide5bitBy31_LUT[poly->getAttributeAlpha()] : 1.0f;
-			
-			for(size_t j = 0; j < polyType; j++)
-			{
-				const GLushort vertIndex = poly->vertIndexes[j];
-				const size_t colorIndex = vertIndex * 4;
-				
-				// Consolidate the vertex color and the poly alpha to our internal color buffer
-				// so that OpenGL can use it.
-				const VERT *vert = &vertList->list[vertIndex];
-				OGLRef.color4fBuffer[colorIndex+0] = material_8bit_to_float[vert->color[0]];
-				OGLRef.color4fBuffer[colorIndex+1] = material_8bit_to_float[vert->color[1]];
-				OGLRef.color4fBuffer[colorIndex+2] = material_8bit_to_float[vert->color[2]];
-				OGLRef.color4fBuffer[colorIndex+3] = thePolyAlpha;
-				
-				// While we're looping through our vertices, add each vertex index to a
-				// buffer. For GFX3D_QUADS and GFX3D_QUAD_STRIP, we also add additional
-				// vertices here to convert them to GL_TRIANGLES, which are much easier
-				// to work with and won't be deprecated in future OpenGL versions.
-				outIndexBuffer[vertIndexCount++] = vertIndex;
-				if (poly->vtxFormat == GFX3D_QUADS || poly->vtxFormat == GFX3D_QUAD_STRIP)
-				{
-					if (j == 2)
-					{
-						outIndexBuffer[vertIndexCount++] = vertIndex;
-					}
-					else if (j == 3)
-					{
-						outIndexBuffer[vertIndexCount++] = poly->vertIndexes[0];
-					}
-				}
-			}
-		}
-	}
-	
-	*outIndexCount = vertIndexCount;
-	
-	return OGLERROR_NOERR;
-}
-
-Render3DError OpenGLRenderer_1_2::EnableVertexAttributes(const VERTLIST *vertList, const GLushort *indexBuffer, const size_t vertIndexCount)
+Render3DError OpenGLRenderer_1_2::EnableVertexAttributes()
 {
 	OGLRenderRef &OGLRef = *this->ref;
 	
 	if (this->isVAOSupported)
 	{
 		glBindVertexArray(OGLRef.vaoGeometryStatesID);
-		glBindBuffer(GL_ARRAY_BUFFER_ARB, OGLRef.vboGeometryVtxID);
-		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, OGLRef.iboGeometryIndexID);
-		
-		glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(VERT) * vertList->count, vertList);
-		glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, vertIndexCount * sizeof(GLushort), indexBuffer);
 	}
 	else
 	{
@@ -1912,24 +1830,9 @@ Render3DError OpenGLRenderer_1_2::EnableVertexAttributes(const VERTLIST *vertLis
 			glEnableVertexAttribArray(OGLVertexAttributeID_Position);
 			glEnableVertexAttribArray(OGLVertexAttributeID_TexCoord0);
 			glEnableVertexAttribArray(OGLVertexAttributeID_Color);
-			
-			if (this->isVBOSupported)
-			{
-				glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, OGLRef.iboGeometryIndexID);
-				glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, vertIndexCount * sizeof(GLushort), OGLRef.vertIndexBuffer);
-				
-				glBindBufferARB(GL_ARRAY_BUFFER_ARB, OGLRef.vboGeometryVtxID);
-				glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(VERT) * vertList->count, vertList);
-				glVertexAttribPointer(OGLVertexAttributeID_Position, 4, GL_FLOAT, GL_FALSE, sizeof(VERT), (const GLvoid *)offsetof(VERT, coord));
-				glVertexAttribPointer(OGLVertexAttributeID_TexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(VERT), (const GLvoid *)offsetof(VERT, texcoord));
-				glVertexAttribPointer(OGLVertexAttributeID_Color, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VERT), (const GLvoid *)offsetof(VERT, color));
-			}
-			else
-			{
-				glVertexAttribPointer(OGLVertexAttributeID_Position, 4, GL_FLOAT, GL_FALSE, sizeof(VERT), &vertList->list[0].coord);
-				glVertexAttribPointer(OGLVertexAttributeID_TexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(VERT), &vertList->list[0].texcoord);
-				glVertexAttribPointer(OGLVertexAttributeID_Color, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VERT), &vertList->list[0].color);
-			}
+			glVertexAttribPointer(OGLVertexAttributeID_Position, 4, GL_FLOAT, GL_FALSE, sizeof(VERT), OGLRef.vtxPtrPosition);
+			glVertexAttribPointer(OGLVertexAttributeID_TexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(VERT), OGLRef.vtxPtrTexCoord);
+			glVertexAttribPointer(OGLVertexAttributeID_Color, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VERT), OGLRef.vtxPtrColor);
 		}
 		else
 		{
@@ -1939,23 +1842,17 @@ Render3DError OpenGLRenderer_1_2::EnableVertexAttributes(const VERTLIST *vertLis
 			
 			if (this->isVBOSupported)
 			{
-				glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, OGLRef.iboGeometryIndexID);
-				glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, vertIndexCount * sizeof(GLushort), OGLRef.vertIndexBuffer);
-				
 				glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
-				glColorPointer(4, GL_FLOAT, 0, OGLRef.color4fBuffer);
-				
+				glColorPointer(4, GL_FLOAT, 0, OGLRef.vtxPtrColor);
 				glBindBufferARB(GL_ARRAY_BUFFER_ARB, OGLRef.vboGeometryVtxID);
-				glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(VERT) * vertList->count, vertList);
-				glVertexPointer(4, GL_FLOAT, sizeof(VERT), (const GLvoid *)offsetof(VERT, coord));
-				glTexCoordPointer(2, GL_FLOAT, sizeof(VERT), (const GLvoid *)offsetof(VERT, texcoord));
 			}
 			else
 			{
-				glVertexPointer(4, GL_FLOAT, sizeof(VERT), &vertList->list[0].coord);
-				glTexCoordPointer(2, GL_FLOAT, sizeof(VERT), &vertList->list[0].texcoord);
-				glColorPointer(4, GL_FLOAT, 0, OGLRef.color4fBuffer);
+				glColorPointer(4, GL_FLOAT, 0, OGLRef.vtxPtrColor);
 			}
+			
+			glVertexPointer(4, GL_FLOAT, sizeof(VERT), OGLRef.vtxPtrPosition);
+			glTexCoordPointer(2, GL_FLOAT, sizeof(VERT), OGLRef.vtxPtrTexCoord);
 		}
 	}
 	
@@ -1982,25 +1879,6 @@ Render3DError OpenGLRenderer_1_2::DisableVertexAttributes()
 			glDisableClientState(GL_COLOR_ARRAY);
 			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 		}
-		
-		if (this->isVBOSupported)
-		{
-			glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
-			glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
-		}
-	}
-	
-	return OGLERROR_NOERR;
-}
-
-Render3DError OpenGLRenderer_1_2::SelectRenderingFramebuffer()
-{
-	OGLRenderRef &OGLRef = *this->ref;
-	
-	if (this->isMultisampledFBOSupported)
-	{
-		OGLRef.selectedRenderingFBO = (CommonSettings.GFX3D_Renderer_Multisample) ? OGLRef.fboMSIntermediateRenderID : OGLRef.fboRenderID;
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, OGLRef.selectedRenderingFBO);
 	}
 	
 	return OGLERROR_NOERR;
@@ -2076,8 +1954,6 @@ Render3DError OpenGLRenderer_1_2::BeginRender(const GFX3D &engine)
 	OGLRenderRef &OGLRef = *this->ref;
 	this->doubleBufferIndex = (this->doubleBufferIndex + 1) & 0x01;
 	
-	this->SelectRenderingFramebuffer();
-	
 	if (this->isShaderSupported)
 	{
 		glUseProgram(OGLRef.programGeometryID);
@@ -2087,9 +1963,6 @@ Render3DError OpenGLRenderer_1_2::BeginRender(const GFX3D &engine)
 		glUniform1i(OGLRef.uniformStateEnableEdgeMarking, (engine.renderState.enableEdgeMarking) ? GL_TRUE : GL_FALSE);
 		glUniform1i(OGLRef.uniformStateUseWDepth, (engine.renderState.wbuffer) ? GL_TRUE : GL_FALSE);
 		glUniform1f(OGLRef.uniformStateAlphaTestRef, divide5bitBy31_LUT[engine.renderState.alphaTestRef]);
-		
-		glEnable(GL_DEPTH_TEST);
-		glEnable(GL_STENCIL_TEST);
 	}
 	else
 	{
@@ -2106,16 +1979,96 @@ Render3DError OpenGLRenderer_1_2::BeginRender(const GFX3D &engine)
 		glLoadIdentity();
 	}
 	
-	if(engine.renderState.enableAlphaBlending)
+	GLushort *indexPtr = NULL;
+	
+	if (this->isVBOSupported)
 	{
-		glEnable(GL_BLEND);
+		glBindBufferARB(GL_ARRAY_BUFFER_ARB, OGLRef.vboGeometryVtxID);
+		glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, OGLRef.iboGeometryIndexID);
+		indexPtr = (GLushort *)glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
 	}
 	else
 	{
-		glDisable(GL_BLEND);
+		// If VBOs aren't supported, we need to use the client-side buffers here.
+		OGLRef.vtxPtrPosition = &engine.vertlist->list[0].coord;
+		OGLRef.vtxPtrTexCoord = &engine.vertlist->list[0].texcoord;
+		OGLRef.vtxPtrColor = (this->isShaderSupported) ? (GLvoid *)&engine.vertlist->list[0].color : OGLRef.color4fBuffer;
+		indexPtr = OGLRef.vertIndexBuffer;
 	}
 	
-	glDepthMask(GL_TRUE);
+	size_t vertIndexCount = 0;
+	
+	for (size_t i = 0; i < engine.polylist->count; i++)
+	{
+		const POLY *thePoly = &engine.polylist->list[engine.indexlist.list[i]];
+		const size_t polyType = thePoly->type;
+		
+		if (this->isShaderSupported)
+		{
+			for (size_t j = 0; j < polyType; j++)
+			{
+				const GLushort vertIndex = thePoly->vertIndexes[j];
+				
+				// While we're looping through our vertices, add each vertex index to
+				// a buffer. For GFX3D_QUADS and GFX3D_QUAD_STRIP, we also add additional
+				// vertices here to convert them to GL_TRIANGLES, which are much easier
+				// to work with and won't be deprecated in future OpenGL versions.
+				indexPtr[vertIndexCount++] = vertIndex;
+				if (thePoly->vtxFormat == GFX3D_QUADS || thePoly->vtxFormat == GFX3D_QUAD_STRIP)
+				{
+					if (j == 2)
+					{
+						indexPtr[vertIndexCount++] = vertIndex;
+					}
+					else if (j == 3)
+					{
+						indexPtr[vertIndexCount++] = thePoly->vertIndexes[0];
+					}
+				}
+			}
+		}
+		else
+		{
+			const GLfloat thePolyAlpha = (!thePoly->isWireframe() && thePoly->isTranslucent()) ? divide5bitBy31_LUT[thePoly->getAttributeAlpha()] : 1.0f;
+			
+			for (size_t j = 0; j < polyType; j++)
+			{
+				const GLushort vertIndex = thePoly->vertIndexes[j];
+				const size_t colorIndex = vertIndex * 4;
+				
+				// Consolidate the vertex color and the poly alpha to our internal color buffer
+				// so that OpenGL can use it.
+				const VERT *vert = &engine.vertlist->list[vertIndex];
+				OGLRef.color4fBuffer[colorIndex+0] = material_8bit_to_float[vert->color[0]];
+				OGLRef.color4fBuffer[colorIndex+1] = material_8bit_to_float[vert->color[1]];
+				OGLRef.color4fBuffer[colorIndex+2] = material_8bit_to_float[vert->color[2]];
+				OGLRef.color4fBuffer[colorIndex+3] = thePolyAlpha;
+				
+				// While we're looping through our vertices, add each vertex index to a
+				// buffer. For GFX3D_QUADS and GFX3D_QUAD_STRIP, we also add additional
+				// vertices here to convert them to GL_TRIANGLES, which are much easier
+				// to work with and won't be deprecated in future OpenGL versions.
+				indexPtr[vertIndexCount++] = vertIndex;
+				if (thePoly->vtxFormat == GFX3D_QUADS || thePoly->vtxFormat == GFX3D_QUAD_STRIP)
+				{
+					if (j == 2)
+					{
+						indexPtr[vertIndexCount++] = vertIndex;
+					}
+					else if (j == 3)
+					{
+						indexPtr[vertIndexCount++] = thePoly->vertIndexes[0];
+					}
+				}
+			}
+		}
+	}
+	
+	if (this->isVBOSupported)
+	{
+		glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
+		glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(VERT) * engine.vertlist->count, engine.vertlist);
+	}
 	
 	return OGLERROR_NOERR;
 }
@@ -2138,9 +2091,19 @@ Render3DError OpenGLRenderer_1_2::RenderGeometry(const GFX3D_State &renderState,
 	
 	if (polyCount > 0)
 	{
-		size_t vertIndexBufferCount = 0;
-		this->SetupVertices(vertList, polyList, indexList, OGLRef.vertIndexBuffer, &vertIndexBufferCount);
-		this->EnableVertexAttributes(vertList, OGLRef.vertIndexBuffer, vertIndexBufferCount);
+		glEnable(GL_DEPTH_TEST);
+		glEnable(GL_STENCIL_TEST);
+		
+		if(renderState.enableAlphaBlending)
+		{
+			glEnable(GL_BLEND);
+		}
+		else
+		{
+			glDisable(GL_BLEND);
+		}
+		
+		this->EnableVertexAttributes();
 		
 		const POLY &firstPoly = polyList->list[indexList->list[0]];
 		u32 lastPolyAttr = firstPoly.polyAttr;
@@ -2153,7 +2116,7 @@ Render3DError OpenGLRenderer_1_2::RenderGeometry(const GFX3D_State &renderState,
 		this->SetupViewport(lastViewport);
 		
 		GLsizei vertIndexCount = 0;
-		GLushort *indexBufferPtr = (this->isVBOSupported) ? 0 : OGLRef.vertIndexBuffer;
+		GLushort *indexBufferPtr = OGLRef.vertIndexBuffer;
 		
 		// Enumerate through all polygons and render
 		for (size_t i = 0; i < polyCount; i++)
@@ -2240,7 +2203,10 @@ Render3DError OpenGLRenderer_1_2::EndRender(const u64 frameCount)
 
 Render3DError OpenGLRenderer_1_2::UpdateToonTable(const u16 *toonTableBuffer)
 {
-	this->UploadToonTable(toonTableBuffer);
+	glActiveTextureARB(GL_TEXTURE0_ARB + OGLTextureUnitID_ToonTable);
+	glBindTexture(GL_TEXTURE_1D, this->ref->texToonTableID);
+	glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 32, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, toonTableBuffer);
+	glActiveTextureARB(GL_TEXTURE0_ARB);
 	
 	return OGLERROR_NOERR;
 }
@@ -2256,44 +2222,46 @@ Render3DError OpenGLRenderer_1_2::ClearUsingImage(const u16 *__restrict colorBuf
 	
 	this->UploadClearImage(colorBuffer, depthBuffer, fogBuffer, polyIDBuffer);
 	
-	if (this->isMultisampledFBOSupported && OGLRef.selectedRenderingFBO == OGLRef.fboMSIntermediateRenderID)
+	if (this->isMultisampledFBOSupported)
 	{
-		glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, OGLRef.fboRenderID);
-		glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, OGLRef.fboMSIntermediateRenderID);
+		OGLRef.selectedRenderingFBO = (CommonSettings.GFX3D_Renderer_Multisample) ? OGLRef.fboMSIntermediateRenderID : OGLRef.fboRenderID;
+		if (OGLRef.selectedRenderingFBO == OGLRef.fboMSIntermediateRenderID)
+		{
+			glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, OGLRef.fboRenderID);
+			glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, OGLRef.fboMSIntermediateRenderID);
+			
+			// It might seem wasteful to be doing a separate glClear(GL_STENCIL_BUFFER_BIT) instead
+			// of simply blitting the stencil buffer with everything else.
+			//
+			// We do this because glBlitFramebufferEXT() for GL_STENCIL_BUFFER_BIT has been tested
+			// to be unsupported on ATI/AMD GPUs running in compatibility mode. So we do the separate
+			// glClear() for GL_STENCIL_BUFFER_BIT to keep these GPUs working.
+			glClearStencil(0);
+			glClear(GL_STENCIL_BUFFER_BIT);
+			
+			// Blit the working depth buffer
+			glReadBuffer(GL_COLOR_ATTACHMENT1_EXT);
+			glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
+			glBlitFramebufferEXT(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+			
+			// Blit the polygon ID buffer
+			glReadBuffer(GL_COLOR_ATTACHMENT2_EXT);
+			glDrawBuffer(GL_COLOR_ATTACHMENT2_EXT);
+			glBlitFramebufferEXT(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+			
+			// Blit the fog buffer
+			glReadBuffer(GL_COLOR_ATTACHMENT3_EXT);
+			glDrawBuffer(GL_COLOR_ATTACHMENT3_EXT);
+			glBlitFramebufferEXT(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+			
+			// Blit the color buffer. Do this last so that color attachment 0 is set to the read FBO.
+			glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
+			glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
+			glBlitFramebufferEXT(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+		}
 		
-		// Blit the color buffer
-		glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
-		glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
-		glBlitFramebufferEXT(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
-		
-		// It might seem wasteful to be doing a separate glClear(GL_STENCIL_BUFFER_BIT) instead
-		// of simply blitting the stencil buffer with everything else.
-		//
-		// We do this because glBlitFramebufferEXT() for GL_STENCIL_BUFFER_BIT has been tested
-		// to be unsupported on ATI/AMD GPUs running in compatibility mode. So we do the separate
-		// glClear() for GL_STENCIL_BUFFER_BIT to keep these GPUs working.
-		glClearStencil(0);
-		glClear(GL_STENCIL_BUFFER_BIT);
-		
-		// Blit the working depth buffer
-		glReadBuffer(GL_COLOR_ATTACHMENT1_EXT);
-		glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
-		glBlitFramebufferEXT(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
-		
-		// Blit the polygon ID buffer
-		glReadBuffer(GL_COLOR_ATTACHMENT2_EXT);
-		glDrawBuffer(GL_COLOR_ATTACHMENT2_EXT);
-		glBlitFramebufferEXT(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
-		
-		// Blit the fog buffer
-		glReadBuffer(GL_COLOR_ATTACHMENT3_EXT);
-		glDrawBuffer(GL_COLOR_ATTACHMENT3_EXT);
-		glBlitFramebufferEXT(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
-		
-		// Reset framebuffer targets
-		glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
+		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, OGLRef.selectedRenderingFBO);
 		glDrawBuffers(4, RenderDrawList);
-		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, OGLRef.fboMSIntermediateRenderID);
 	}
 	
 	return OGLERROR_NOERR;
@@ -2301,6 +2269,16 @@ Render3DError OpenGLRenderer_1_2::ClearUsingImage(const u16 *__restrict colorBuf
 
 Render3DError OpenGLRenderer_1_2::ClearUsingValues(const FragmentColor &clearColor, const FragmentAttributes &clearAttributes) const
 {
+	OGLRenderRef &OGLRef = *this->ref;
+	
+	if (this->isMultisampledFBOSupported)
+	{
+		OGLRef.selectedRenderingFBO = (CommonSettings.GFX3D_Renderer_Multisample) ? OGLRef.fboMSIntermediateRenderID : OGLRef.fboRenderID;
+		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, OGLRef.selectedRenderingFBO);
+	}
+	
+	glDepthMask(GL_TRUE);
+	
 	if (this->isShaderSupported && this->isFBOSupported)
 	{
 		glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); // texGColorID
@@ -2344,21 +2322,6 @@ Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly)
 	OGLRenderRef &OGLRef = *this->ref;
 	const PolygonAttributes attr = thePoly.getAttributes();
 	
-	// Set up polygon attributes
-	if (this->isShaderSupported)
-	{
-		glUniform1i(OGLRef.uniformPolyMode, attr.polygonMode);
-		glUniform1i(OGLRef.uniformPolyEnableFog, (attr.enableRenderFog) ? GL_TRUE : GL_FALSE);
-		glUniform1f(OGLRef.uniformPolyAlpha, (!attr.isWireframe && attr.isTranslucent) ? divide5bitBy31_LUT[attr.alpha] : 1.0f);
-		glUniform1i(OGLRef.uniformPolyID, attr.polygonID);
-	}
-	else
-	{
-		// Set the texture blending mode
-		static const GLint oglTexBlendMode[4] = {GL_MODULATE, GL_DECAL, GL_MODULATE, GL_MODULATE};
-		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, oglTexBlendMode[attr.polygonMode]);
-	}
-	
 	// Set up depth test mode
 	static const GLenum oglDepthFunc[2] = {GL_LESS, GL_EQUAL};
 	glDepthFunc(oglDepthFunc[attr.enableDepthTest]);
@@ -2430,11 +2393,22 @@ Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly)
 	
 	glDepthMask(enableDepthWrite);
 	
+	// Set up polygon attributes
 	if (this->isShaderSupported)
 	{
+		glUniform1i(OGLRef.uniformPolyMode, attr.polygonMode);
+		glUniform1i(OGLRef.uniformPolyEnableFog, (attr.enableRenderFog) ? GL_TRUE : GL_FALSE);
+		glUniform1f(OGLRef.uniformPolyAlpha, (!attr.isWireframe && attr.isTranslucent) ? divide5bitBy31_LUT[attr.alpha] : 1.0f);
+		glUniform1i(OGLRef.uniformPolyID, attr.polygonID);
 		glUniform1i(OGLRef.uniformPolyEnableDepthWrite, enableDepthWrite);
 		glUniform1i(OGLRef.uniformPolySetNewDepthForTranslucent, (attr.enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE);
 	}
+	else
+	{
+		// Set the texture blending mode
+		static const GLint oglTexBlendMode[4] = {GL_MODULATE, GL_DECAL, GL_MODULATE, GL_MODULATE};
+		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, oglTexBlendMode[attr.polygonMode]);
+	}
 	
 	return OGLERROR_NOERR;
 }
@@ -2542,7 +2516,7 @@ Render3DError OpenGLRenderer_1_2::Reset()
 		memset(this->GPU_screen3D[i], 0, sizeof(this->GPU_screen3D[i]));
 	}
 	
-	if(!this->isShaderSupported)
+	if (!this->isShaderSupported)
 	{
 		glEnable(GL_NORMALIZE);
 		glEnable(GL_TEXTURE_1D);
@@ -2550,15 +2524,26 @@ Render3DError OpenGLRenderer_1_2::Reset()
 		glAlphaFunc(GL_GREATER, 0);
 		glEnable(GL_ALPHA_TEST);
 		glEnable(GL_BLEND);
-		
+	}
+	
+	if (OGLRef.color4fBuffer != NULL)
+	{
 		memset(OGLRef.color4fBuffer, 0, VERTLIST_SIZE * 4 * sizeof(GLfloat));
 	}
 	
-	memset(OGLRef.vertIndexBuffer, 0, OGLRENDER_VERT_INDEX_BUFFER_COUNT * sizeof(GLushort));
+	if (OGLRef.vertIndexBuffer != NULL)
+	{
+		memset(OGLRef.vertIndexBuffer, 0, OGLRENDER_VERT_INDEX_BUFFER_COUNT * sizeof(GLushort));
+	}
+	
 	this->currTexture = NULL;
 	this->doubleBufferIndex = 0;
 	this->_currentPolyIndex = 0;
 	
+	OGLRef.vtxPtrPosition = (GLvoid *)offsetof(VERT, coord);
+	OGLRef.vtxPtrTexCoord = (GLvoid *)offsetof(VERT, texcoord);
+	OGLRef.vtxPtrColor = (this->isShaderSupported) ? (GLvoid *)offsetof(VERT, color) : OGLRef.color4fBuffer;
+	
 	return OGLERROR_NOERR;
 }
 
@@ -2598,7 +2583,7 @@ Render3DError OpenGLRenderer_1_2::RenderFinish()
 	return OGLERROR_NOERR;
 }
 
-Render3DError OpenGLRenderer_1_3::UploadToonTable(const u16 *toonTableBuffer)
+Render3DError OpenGLRenderer_1_3::UpdateToonTable(const u16 *toonTableBuffer)
 {
 	glActiveTexture(GL_TEXTURE0 + OGLTextureUnitID_ToonTable);
 	glBindTexture(GL_TEXTURE_1D, this->ref->texToonTableID);
@@ -2613,19 +2598,21 @@ Render3DError OpenGLRenderer_1_3::UploadClearImage(const u16 *__restrict colorBu
 	OGLRenderRef &OGLRef = *this->ref;
 	
 	static GLuint depth[GFX3D_FRAMEBUFFER_WIDTH * GFX3D_FRAMEBUFFER_HEIGHT];
+	static GLuint depthStencil[GFX3D_FRAMEBUFFER_WIDTH * GFX3D_FRAMEBUFFER_HEIGHT];
 	static GLuint fogAttributes[GFX3D_FRAMEBUFFER_WIDTH * GFX3D_FRAMEBUFFER_HEIGHT];
 	static GLuint polyID[GFX3D_FRAMEBUFFER_WIDTH * GFX3D_FRAMEBUFFER_HEIGHT];
 	
 	for (size_t i = 0; i < GFX3D_FRAMEBUFFER_WIDTH * GFX3D_FRAMEBUFFER_HEIGHT; i++)
 	{
 		depth[i] = depthBuffer[i] | 0xFF000000;
+		depthStencil[i] = depthBuffer[i] << 8;
 		fogAttributes[i] = (fogBuffer[i]) ? 0xFF0000FF : 0xFF000000;
 		polyID[i] = (GLuint)polyIDBuffer[i] | 0xFF000000;
 	}
 
 	glActiveTexture(GL_TEXTURE0 + OGLTextureUnitID_GColor);
 	glBindTexture(GL_TEXTURE_2D, OGLRef.texGDepthStencilID);
-	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, depthBuffer);
+	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, depthStencil);
 	glBindTexture(GL_TEXTURE_2D, OGLRef.texGColorID);
 	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, colorBuffer);
 	
@@ -2672,7 +2659,11 @@ Render3DError OpenGLRenderer_1_4::InitFinalRenderStates(const std::set<std::stri
 	// because OpenGL needs 4-colors per vertex to support translucency. (The DS
 	// uses 3-colors per vertex, and adds alpha through the poly, so we can't
 	// simply reference the colors+alpha from just the vertices by themselves.)
-	OGLRef.color4fBuffer = this->isShaderSupported ? NULL : new GLfloat[VERTLIST_SIZE * 4];
+	OGLRef.color4fBuffer = (this->isShaderSupported) ? NULL : new GLfloat[VERTLIST_SIZE * 4];
+	
+	// If VBOs aren't supported, then we need to create the index buffer on the
+	// client side so that we have a buffer to update.
+	OGLRef.vertIndexBuffer = (this->isVBOSupported) ? NULL : new GLushort[OGLRENDER_VERT_INDEX_BUFFER_COUNT];
 	
 	return OGLERROR_NOERR;
 }
@@ -2794,36 +2785,24 @@ Render3DError OpenGLRenderer_1_5::CreateVAOs()
 	return OGLERROR_NOERR;
 }
 
-Render3DError OpenGLRenderer_1_5::EnableVertexAttributes(const VERTLIST *vertList, const GLushort *indexBuffer, const size_t vertIndexCount)
+Render3DError OpenGLRenderer_1_5::EnableVertexAttributes()
 {
 	OGLRenderRef &OGLRef = *this->ref;
 	
 	if (this->isVAOSupported)
 	{
 		glBindVertexArray(OGLRef.vaoGeometryStatesID);
-		glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID);
-		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID);
-		
-		glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(VERT) * vertList->count, vertList);
-		glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, vertIndexCount * sizeof(GLushort), indexBuffer);
 	}
 	else
 	{
 		if (this->isShaderSupported)
 		{
-			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID);
-			glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, vertIndexCount * sizeof(GLushort), indexBuffer);
-			
-			glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID);
-			glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(VERT) * vertList->count, vertList);
-			
 			glEnableVertexAttribArray(OGLVertexAttributeID_Position);
 			glEnableVertexAttribArray(OGLVertexAttributeID_TexCoord0);
 			glEnableVertexAttribArray(OGLVertexAttributeID_Color);
-			
-			glVertexAttribPointer(OGLVertexAttributeID_Position, 4, GL_FLOAT, GL_FALSE, sizeof(VERT), (const GLvoid *)offsetof(VERT, coord));
-			glVertexAttribPointer(OGLVertexAttributeID_TexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(VERT), (const GLvoid *)offsetof(VERT, texcoord));
-			glVertexAttribPointer(OGLVertexAttributeID_Color, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VERT), (const GLvoid *)offsetof(VERT, color));
+			glVertexAttribPointer(OGLVertexAttributeID_Position, 4, GL_FLOAT, GL_FALSE, sizeof(VERT), OGLRef.vtxPtrPosition);
+			glVertexAttribPointer(OGLVertexAttributeID_TexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(VERT), OGLRef.vtxPtrTexCoord);
+			glVertexAttribPointer(OGLVertexAttributeID_Color, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VERT), OGLRef.vtxPtrColor);
 		}
 		else
 		{
@@ -2831,16 +2810,12 @@ Render3DError OpenGLRenderer_1_5::EnableVertexAttributes(const VERTLIST *vertLis
 			glEnableClientState(GL_COLOR_ARRAY);
 			glEnableClientState(GL_VERTEX_ARRAY);
 			
-			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID);
-			glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, vertIndexCount * sizeof(GLushort), indexBuffer);
-			
 			glBindBuffer(GL_ARRAY_BUFFER, 0);
-			glColorPointer(4, GL_FLOAT, 0, OGLRef.color4fBuffer);
+			glColorPointer(4, GL_FLOAT, 0, OGLRef.vtxPtrColor);
 			
 			glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID);
-			glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(VERT) * vertList->count, vertList);
-			glVertexPointer(4, GL_FLOAT, sizeof(VERT), (const GLvoid *)offsetof(VERT, coord));
-			glTexCoordPointer(2, GL_FLOAT, sizeof(VERT), (const GLvoid *)offsetof(VERT, texcoord));
+			glVertexPointer(4, GL_FLOAT, sizeof(VERT), OGLRef.vtxPtrPosition);
+			glTexCoordPointer(2, GL_FLOAT, sizeof(VERT), OGLRef.vtxPtrTexCoord);
 		}
 	}
 	
@@ -2867,14 +2842,81 @@ Render3DError OpenGLRenderer_1_5::DisableVertexAttributes()
 			glDisableClientState(GL_COLOR_ARRAY);
 			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 		}
-		
-		glBindBuffer(GL_ARRAY_BUFFER, 0);
-		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 	}
 	
 	return OGLERROR_NOERR;
 }
 
+Render3DError OpenGLRenderer_1_5::BeginRender(const GFX3D &engine)
+{
+	OGLRenderRef &OGLRef = *this->ref;
+	this->doubleBufferIndex = (this->doubleBufferIndex + 1) & 0x01;
+	
+	if (this->isShaderSupported)
+	{
+		glUseProgram(OGLRef.programGeometryID);
+		glUniform1i(OGLRef.uniformStateToonShadingMode, engine.renderState.shading);
+		glUniform1i(OGLRef.uniformStateEnableAlphaTest, (engine.renderState.enableAlphaTest) ? GL_TRUE : GL_FALSE);
+		glUniform1i(OGLRef.uniformStateEnableAntialiasing, (engine.renderState.enableAntialiasing) ? GL_TRUE : GL_FALSE);
+		glUniform1i(OGLRef.uniformStateEnableEdgeMarking, (engine.renderState.enableEdgeMarking) ? GL_TRUE : GL_FALSE);
+		glUniform1i(OGLRef.uniformStateUseWDepth, (engine.renderState.wbuffer) ? GL_TRUE : GL_FALSE);
+		glUniform1f(OGLRef.uniformStateAlphaTestRef, divide5bitBy31_LUT[engine.renderState.alphaTestRef]);
+	}
+	else
+	{
+		if(engine.renderState.enableAlphaTest && (engine.renderState.alphaTestRef > 0))
+		{
+			glAlphaFunc(GL_GEQUAL, divide5bitBy31_LUT[engine.renderState.alphaTestRef]);
+		}
+		else
+		{
+			glAlphaFunc(GL_GREATER, 0);
+		}
+		
+		glMatrixMode(GL_PROJECTION);
+		glLoadIdentity();
+	}
+	
+	glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID);
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID);
+	
+	size_t vertIndexCount = 0;
+	GLushort *indexPtr = (GLushort *)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
+	
+	for (size_t i = 0; i < engine.polylist->count; i++)
+	{
+		const POLY *thePoly = &engine.polylist->list[engine.indexlist.list[i]];
+		const size_t polyType = thePoly->type;
+		
+		for (size_t j = 0; j < polyType; j++)
+		{
+			const GLushort vertIndex = thePoly->vertIndexes[j];
+			
+			// While we're looping through our vertices, add each vertex index to
+			// a buffer. For GFX3D_QUADS and GFX3D_QUAD_STRIP, we also add additional
+			// vertices here to convert them to GL_TRIANGLES, which are much easier
+			// to work with and won't be deprecated in future OpenGL versions.
+			indexPtr[vertIndexCount++] = vertIndex;
+			if (thePoly->vtxFormat == GFX3D_QUADS || thePoly->vtxFormat == GFX3D_QUAD_STRIP)
+			{
+				if (j == 2)
+				{
+					indexPtr[vertIndexCount++] = vertIndex;
+				}
+				else if (j == 3)
+				{
+					indexPtr[vertIndexCount++] = thePoly->vertIndexes[0];
+				}
+			}
+		}
+	}
+	
+	glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
+	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(VERT) * engine.vertlist->count, engine.vertlist);
+	
+	return OGLERROR_NOERR;
+}
+
 Render3DError OpenGLRenderer_1_5::ReadBackPixels()
 {
 	const size_t i = this->doubleBufferIndex;
@@ -3044,6 +3086,9 @@ Render3DError OpenGLRenderer_2_0::InitFinalRenderStates(const std::set<std::stri
 	// Ignore our color buffer since we'll transfer the polygon alpha through a uniform.
 	OGLRef.color4fBuffer = NULL;
 	
+	// VBOs are supported here, so just use the index buffer on the GPU.
+	OGLRef.vertIndexBuffer = NULL;
+	
 	return OGLERROR_NOERR;
 }
 
@@ -3288,72 +3333,22 @@ Render3DError OpenGLRenderer_2_0::DestroyPostprocessingPrograms()
 	return OGLERROR_NOERR;
 }
 
-Render3DError OpenGLRenderer_2_0::SetupVertices(const VERTLIST *vertList, const POLYLIST *polyList, const INDEXLIST *indexList, GLushort *outIndexBuffer, size_t *outIndexCount)
-{
-	const size_t polyCount = polyList->count;
-	size_t vertIndexCount = 0;
-	
-	for(size_t i = 0; i < polyCount; i++)
-	{
-		const POLY *poly = &polyList->list[indexList->list[i]];
-		const size_t polyType = poly->type;
-		
-		for(size_t j = 0; j < polyType; j++)
-		{
-			const GLushort vertIndex = poly->vertIndexes[j];
-			
-			// While we're looping through our vertices, add each vertex index to
-			// a buffer. For GFX3D_QUADS and GFX3D_QUAD_STRIP, we also add additional
-			// vertices here to convert them to GL_TRIANGLES, which are much easier
-			// to work with and won't be deprecated in future OpenGL versions.
-			outIndexBuffer[vertIndexCount++] = vertIndex;
-			if (poly->vtxFormat == GFX3D_QUADS || poly->vtxFormat == GFX3D_QUAD_STRIP)
-			{
-				if (j == 2)
-				{
-					outIndexBuffer[vertIndexCount++] = vertIndex;
-				}
-				else if (j == 3)
-				{
-					outIndexBuffer[vertIndexCount++] = poly->vertIndexes[0];
-				}
-			}
-		}
-	}
-	
-	*outIndexCount = vertIndexCount;
-	
-	return OGLERROR_NOERR;
-}
-
-Render3DError OpenGLRenderer_2_0::EnableVertexAttributes(const VERTLIST *vertList, const GLushort *indexBuffer, const size_t vertIndexCount)
+Render3DError OpenGLRenderer_2_0::EnableVertexAttributes()
 {
 	OGLRenderRef &OGLRef = *this->ref;
 	
 	if (this->isVAOSupported)
 	{
 		glBindVertexArray(OGLRef.vaoGeometryStatesID);
-		glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID);
-		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID);
-		
-		glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(VERT) * vertList->count, vertList);
-		glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, vertIndexCount * sizeof(GLushort), indexBuffer);
 	}
 	else
 	{
-		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID);
-		glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, vertIndexCount * sizeof(GLushort), indexBuffer);
-		
-		glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID);
-		glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(VERT) * vertList->count, vertList);
-		
 		glEnableVertexAttribArray(OGLVertexAttributeID_Position);
 		glEnableVertexAttribArray(OGLVertexAttributeID_TexCoord0);
 		glEnableVertexAttribArray(OGLVertexAttributeID_Color);
-		
-		glVertexAttribPointer(OGLVertexAttributeID_Position, 4, GL_FLOAT, GL_FALSE, sizeof(VERT), (const GLvoid *)offsetof(VERT, coord));
-		glVertexAttribPointer(OGLVertexAttributeID_TexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(VERT), (const GLvoid *)offsetof(VERT, texcoord));
-		glVertexAttribPointer(OGLVertexAttributeID_Color, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VERT), (const GLvoid *)offsetof(VERT, color));
+		glVertexAttribPointer(OGLVertexAttributeID_Position, 4, GL_FLOAT, GL_FALSE, sizeof(VERT), OGLRef.vtxPtrPosition);
+		glVertexAttribPointer(OGLVertexAttributeID_TexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(VERT), OGLRef.vtxPtrTexCoord);
+		glVertexAttribPointer(OGLVertexAttributeID_Color, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VERT), OGLRef.vtxPtrColor);
 	}
 	
 	return OGLERROR_NOERR;
@@ -3370,9 +3365,6 @@ Render3DError OpenGLRenderer_2_0::DisableVertexAttributes()
 		glDisableVertexAttribArray(OGLVertexAttributeID_Position);
 		glDisableVertexAttribArray(OGLVertexAttributeID_TexCoord0);
 		glDisableVertexAttribArray(OGLVertexAttributeID_Color);
-		
-		glBindBuffer(GL_ARRAY_BUFFER, 0);
-		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 	}
 	
 	return OGLERROR_NOERR;
@@ -3383,8 +3375,7 @@ Render3DError OpenGLRenderer_2_0::BeginRender(const GFX3D &engine)
 	OGLRenderRef &OGLRef = *this->ref;
 	this->doubleBufferIndex = (this->doubleBufferIndex + 1) & 0x01;
 	
-	this->SelectRenderingFramebuffer();
-	
+	// Setup render states
 	glUseProgram(OGLRef.programGeometryID);
 	glUniform1i(OGLRef.uniformStateToonShadingMode, engine.renderState.shading);
 	glUniform1i(OGLRef.uniformStateEnableAlphaTest, (engine.renderState.enableAlphaTest) ? GL_TRUE : GL_FALSE);
@@ -3393,19 +3384,42 @@ Render3DError OpenGLRenderer_2_0::BeginRender(const GFX3D &engine)
 	glUniform1i(OGLRef.uniformStateUseWDepth, (engine.renderState.wbuffer) ? GL_TRUE : GL_FALSE);
 	glUniform1f(OGLRef.uniformStateAlphaTestRef, divide5bitBy31_LUT[engine.renderState.alphaTestRef]);
 	
-	glEnable(GL_DEPTH_TEST);
-	glEnable(GL_STENCIL_TEST);
+	glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID);
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID);
 	
-	if(engine.renderState.enableAlphaBlending)
+	size_t vertIndexCount = 0;
+	GLushort *indexPtr = (GLushort *)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
+	
+	for (size_t i = 0; i < engine.polylist->count; i++)
 	{
-		glEnable(GL_BLEND);
-	}
-	else
-	{
-		glDisable(GL_BLEND);
+		const POLY *thePoly = &engine.polylist->list[engine.indexlist.list[i]];
+		const size_t polyType = thePoly->type;
+		
+		for (size_t j = 0; j < polyType; j++)
+		{
+			const GLushort vertIndex = thePoly->vertIndexes[j];
+			
+			// While we're looping through our vertices, add each vertex index to
+			// a buffer. For GFX3D_QUADS and GFX3D_QUAD_STRIP, we also add additional
+			// vertices here to convert them to GL_TRIANGLES, which are much easier
+			// to work with and won't be deprecated in future OpenGL versions.
+			indexPtr[vertIndexCount++] = vertIndex;
+			if (thePoly->vtxFormat == GFX3D_QUADS || thePoly->vtxFormat == GFX3D_QUAD_STRIP)
+			{
+				if (j == 2)
+				{
+					indexPtr[vertIndexCount++] = vertIndex;
+				}
+				else if (j == 3)
+				{
+					indexPtr[vertIndexCount++] = thePoly->vertIndexes[0];
+				}
+			}
+		}
 	}
 	
-	glDepthMask(GL_TRUE);
+	glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
+	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(VERT) * engine.vertlist->count, engine.vertlist);
 	
 	return OGLERROR_NOERR;
 }
@@ -3574,12 +3588,6 @@ Render3DError OpenGLRenderer_2_0::SetupPolygon(const POLY &thePoly)
 	OGLRenderRef &OGLRef = *this->ref;
 	const PolygonAttributes attr = thePoly.getAttributes();
 	
-	// Set up polygon attributes
-	glUniform1i(OGLRef.uniformPolyMode, attr.polygonMode);
-	glUniform1i(OGLRef.uniformPolyEnableFog, (attr.enableRenderFog) ? GL_TRUE : GL_FALSE);
-	glUniform1f(OGLRef.uniformPolyAlpha, (!attr.isWireframe && attr.isTranslucent) ? divide5bitBy31_LUT[attr.alpha] : 1.0f);
-	glUniform1i(OGLRef.uniformPolyID, attr.polygonID);
-	
 	// Set up depth test mode
 	static const GLenum oglDepthFunc[2] = {GL_LESS, GL_EQUAL};
 	glDepthFunc(oglDepthFunc[attr.enableDepthTest]);
@@ -3650,6 +3658,12 @@ Render3DError OpenGLRenderer_2_0::SetupPolygon(const POLY &thePoly)
 	}
 	
 	glDepthMask(enableDepthWrite);
+	
+	// Set up polygon attributes
+	glUniform1i(OGLRef.uniformPolyMode, attr.polygonMode);
+	glUniform1i(OGLRef.uniformPolyEnableFog, (attr.enableRenderFog) ? GL_TRUE : GL_FALSE);
+	glUniform1f(OGLRef.uniformPolyAlpha, (!attr.isWireframe && attr.isTranslucent) ? divide5bitBy31_LUT[attr.alpha] : 1.0f);
+	glUniform1i(OGLRef.uniformPolyID, attr.polygonID);
 	glUniform1i(OGLRef.uniformPolyEnableDepthWrite, enableDepthWrite);
 	glUniform1i(OGLRef.uniformPolySetNewDepthForTranslucent, (attr.enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE);
 	
diff --git a/desmume/src/OGLRender.h b/desmume/src/OGLRender.h
index 92be910fe..ac63e2270 100644
--- a/desmume/src/OGLRender.h
+++ b/desmume/src/OGLRender.h
@@ -280,7 +280,7 @@ EXTERNOGLEXT(PFNGLTEXBUFFERPROC, glTexBuffer) // Core in v3.1
 #define OGLRENDER_MINIMUM_DRIVER_VERSION_REQUIRED_REVISION		0
 
 #define OGLRENDER_MAX_MULTISAMPLES			16
-#define OGLRENDER_VERT_INDEX_BUFFER_COUNT	131072
+#define OGLRENDER_VERT_INDEX_BUFFER_COUNT	(POLYLIST_SIZE * 6)
 
 enum OGLVertexAttributeID
 {
@@ -477,7 +477,12 @@ struct OGLRenderRef
 	
 	// Client-side Buffers
 	GLfloat *color4fBuffer;
-	CACHE_ALIGN GLushort vertIndexBuffer[OGLRENDER_VERT_INDEX_BUFFER_COUNT];
+	GLushort *vertIndexBuffer;
+	
+	// Vertex Attributes Pointers
+	GLvoid *vtxPtrPosition;
+	GLvoid *vtxPtrTexCoord;
+	GLvoid *vtxPtrColor;
 };
 
 struct GFX3D_State;
@@ -580,15 +585,12 @@ protected:
 	virtual Render3DError InitGeometryProgramShaderLocations() = 0;
 	virtual Render3DError CreateToonTable() = 0;
 	virtual Render3DError DestroyToonTable() = 0;
-	virtual Render3DError UploadToonTable(const u16 *toonTableBuffer) = 0;
 	virtual Render3DError UploadClearImage(const u16 *__restrict colorBuffer, const u32 *__restrict depthBuffer, const bool *__restrict fogBuffer, const u8 *__restrict polyIDBuffer) = 0;
 	
 	virtual void GetExtensionSet(std::set<std::string> *oglExtensionSet) = 0;
 	virtual Render3DError ExpandFreeTextures() = 0;
-	virtual Render3DError SetupVertices(const VERTLIST *vertList, const POLYLIST *polyList, const INDEXLIST *indexList, GLushort *outIndexBuffer, size_t *outIndexCount) = 0;
-	virtual Render3DError EnableVertexAttributes(const VERTLIST *vertList, const GLushort *indexBuffer, const size_t vertIndexCount) = 0;
+	virtual Render3DError EnableVertexAttributes() = 0;
 	virtual Render3DError DisableVertexAttributes() = 0;
-	virtual Render3DError SelectRenderingFramebuffer() = 0;
 	virtual Render3DError DownsampleFBO() = 0;
 	virtual Render3DError ReadBackPixels() = 0;
 	
@@ -641,15 +643,12 @@ protected:
 	
 	virtual Render3DError CreateToonTable();
 	virtual Render3DError DestroyToonTable();
-	virtual Render3DError UploadToonTable(const u16 *toonTableBuffer);
 	virtual Render3DError UploadClearImage(const u16 *__restrict colorBuffer, const u32 *__restrict depthBuffer, const bool *__restrict fogBuffer, const u8 *__restrict polyIDBuffer);
 	
 	virtual void GetExtensionSet(std::set<std::string> *oglExtensionSet);
 	virtual Render3DError ExpandFreeTextures();
-	virtual Render3DError SetupVertices(const VERTLIST *vertList, const POLYLIST *polyList, const INDEXLIST *indexList, GLushort *outIndexBuffer, size_t *outIndexCount);
-	virtual Render3DError EnableVertexAttributes(const VERTLIST *vertList, const GLushort *indexBuffer, const size_t vertIndexCount);
+	virtual Render3DError EnableVertexAttributes();
 	virtual Render3DError DisableVertexAttributes();
-	virtual Render3DError SelectRenderingFramebuffer();
 	virtual Render3DError DownsampleFBO();
 	virtual Render3DError ReadBackPixels();
 	
@@ -681,8 +680,10 @@ public:
 class OpenGLRenderer_1_3 : public OpenGLRenderer_1_2
 {
 protected:
-	virtual Render3DError UploadToonTable(const u16 *toonTableBuffer);
 	virtual Render3DError UploadClearImage(const u16 *__restrict colorBuffer, const u32 *__restrict depthBuffer, const bool *__restrict fogBuffer, const u8 *__restrict polyIDBuffer);
+	
+public:
+	virtual Render3DError UpdateToonTable(const u16 *toonTableBuffer);
 };
 
 class OpenGLRenderer_1_4 : public OpenGLRenderer_1_3
@@ -700,8 +701,9 @@ protected:
 	virtual void DestroyPBOs();
 	virtual Render3DError CreateVAOs();
 	
-	virtual Render3DError EnableVertexAttributes(const VERTLIST *vertList, const GLushort *indexBuffer, const size_t vertIndexCount);
+	virtual Render3DError EnableVertexAttributes();
 	virtual Render3DError DisableVertexAttributes();
+	virtual Render3DError BeginRender(const GFX3D &engine);
 	virtual Render3DError ReadBackPixels();
 		
 public:
@@ -722,8 +724,7 @@ protected:
 	virtual Render3DError InitFogProgramShaderLocations();
 	virtual Render3DError DestroyPostprocessingPrograms();
 	
-	virtual Render3DError SetupVertices(const VERTLIST *vertList, const POLYLIST *polyList, const INDEXLIST *indexList, GLushort *outIndexBuffer, size_t *outIndexCount);
-	virtual Render3DError EnableVertexAttributes(const VERTLIST *vertList, const GLushort *indexBuffer, const size_t vertIndexCount);
+	virtual Render3DError EnableVertexAttributes();
 	virtual Render3DError DisableVertexAttributes();
 	
 	virtual Render3DError BeginRender(const GFX3D &engine);
diff --git a/desmume/src/OGLRender_3_2.cpp b/desmume/src/OGLRender_3_2.cpp
index 5235fe721..13e26d3d0 100644
--- a/desmume/src/OGLRender_3_2.cpp
+++ b/desmume/src/OGLRender_3_2.cpp
@@ -990,17 +990,9 @@ void OpenGLRenderer_3_2::GetExtensionSet(std::set<std::string> *oglExtensionSet)
 	}
 }
 
-Render3DError OpenGLRenderer_3_2::EnableVertexAttributes(const VERTLIST *vertList, const GLushort *indexBuffer, const size_t vertIndexCount)
+Render3DError OpenGLRenderer_3_2::EnableVertexAttributes()
 {
-	OGLRenderRef &OGLRef = *this->ref;
-	
-	glBindVertexArray(OGLRef.vaoGeometryStatesID);
-	glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID);
-	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID);
-	
-	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(VERT) * vertList->count, vertList);
-	glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, vertIndexCount * sizeof(GLushort), indexBuffer);
-	
+	glBindVertexArray(this->ref->vaoGeometryStatesID);
 	return OGLERROR_NOERR;
 }
 
@@ -1010,17 +1002,6 @@ Render3DError OpenGLRenderer_3_2::DisableVertexAttributes()
 	return OGLERROR_NOERR;
 }
 
-Render3DError OpenGLRenderer_3_2::SelectRenderingFramebuffer()
-{
-	OGLRenderRef &OGLRef = *this->ref;
-	
-	OGLRef.selectedRenderingFBO = (CommonSettings.GFX3D_Renderer_Multisample) ? OGLRef.fboMSIntermediateRenderID : OGLRef.fboRenderID;
-	glBindFramebuffer(GL_FRAMEBUFFER, OGLRef.selectedRenderingFBO);
-	glDrawBuffers(4, RenderDrawList);
-	
-	return OGLERROR_NOERR;
-}
-
 Render3DError OpenGLRenderer_3_2::DownsampleFBO()
 {
 	OGLRenderRef &OGLRef = *this->ref;
@@ -1141,13 +1122,19 @@ Render3DError OpenGLRenderer_3_2::BeginRender(const GFX3D &engine)
 	// Do per-poly setup
 	glActiveTexture(GL_TEXTURE0 + OGLTextureUnitID_PolyStates);
 	glBindTexture(GL_TEXTURE_BUFFER, OGLRef.texPolyStatesID);
+	
+	glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID);
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID);
 	glBindBuffer(GL_TEXTURE_BUFFER, OGLRef.tboPolyStatesID);
 	
+	size_t vertIndexCount = 0;
+	GLushort *indexPtr = (GLushort *)glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, engine.polylist->count * 6 * sizeof(GLushort), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
 	OGLPolyStates *polyStates = (OGLPolyStates *)glMapBufferRange(GL_TEXTURE_BUFFER, 0, engine.polylist->count * sizeof(OGLPolyStates), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
 	
 	for (size_t i = 0; i < engine.polylist->count; i++)
 	{
 		const POLY *thePoly = &engine.polylist->list[engine.indexlist.list[i]];
+		const size_t polyType = thePoly->type;
 		PolygonAttributes polyAttr = thePoly->getAttributes();
 		PolygonTexParams texParams = thePoly->getTexParams();
 		
@@ -1155,34 +1142,41 @@ Render3DError OpenGLRenderer_3_2::BeginRender(const GFX3D &engine)
 		polyStates[i].enableFog = (polyAttr.enableRenderFog) ? GL_TRUE : GL_FALSE;
 		polyStates[i].enableDepthWrite = ((!polyAttr.isTranslucent || polyAttr.enableAlphaDepthWrite) && !(polyAttr.polygonMode == 3 && polyAttr.polygonID == 0)) ? GL_TRUE : GL_FALSE;
 		polyStates[i].setNewDepthForTranslucent = (polyAttr.enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE;
-		
 		polyStates[i].polyAlpha = (!polyAttr.isWireframe && polyAttr.isTranslucent) ? polyAttr.alpha : 0x1F;
 		polyStates[i].polyMode = polyAttr.polygonMode;
 		polyStates[i].polyID = polyAttr.polygonID;
 		polyStates[i].texSizeS = texParams.sizeS;
 		polyStates[i].texSizeT = texParams.sizeT;
+		
+		for (size_t j = 0; j < polyType; j++)
+		{
+			const GLushort vertIndex = thePoly->vertIndexes[j];
+			
+			// While we're looping through our vertices, add each vertex index to
+			// a buffer. For GFX3D_QUADS and GFX3D_QUAD_STRIP, we also add additional
+			// vertices here to convert them to GL_TRIANGLES, which are much easier
+			// to work with and won't be deprecated in future OpenGL versions.
+			indexPtr[vertIndexCount++] = vertIndex;
+			if (thePoly->vtxFormat == GFX3D_QUADS || thePoly->vtxFormat == GFX3D_QUAD_STRIP)
+			{
+				if (j == 2)
+				{
+					indexPtr[vertIndexCount++] = vertIndex;
+				}
+				else if (j == 3)
+				{
+					indexPtr[vertIndexCount++] = thePoly->vertIndexes[0];
+				}
+			}
+		}
 	}
 	
+	glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
 	glUnmapBuffer(GL_TEXTURE_BUFFER);
+	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(VERT) * engine.vertlist->count, engine.vertlist);
 	
-	// Set up remaining framebuffer states
-	this->SelectRenderingFramebuffer();
 	glUseProgram(OGLRef.programGeometryID);
-	
-	glEnable(GL_DEPTH_TEST);
-	glEnable(GL_STENCIL_TEST);
-	
-	if(engine.renderState.enableAlphaBlending)
-	{
-		glEnable(GL_BLEND);
-	}
-	else
-	{
-		glDisable(GL_BLEND);
-	}
-	
-	glDepthMask(GL_TRUE);
-	
+		
 	return OGLERROR_NOERR;
 }
 
@@ -1253,16 +1247,12 @@ Render3DError OpenGLRenderer_3_2::ClearUsingImage(const u16 *__restrict colorBuf
 	
 	this->UploadClearImage(colorBuffer, depthBuffer, fogBuffer, polyIDBuffer);
 	
+	OGLRef.selectedRenderingFBO = (CommonSettings.GFX3D_Renderer_Multisample) ? OGLRef.fboMSIntermediateRenderID : OGLRef.fboRenderID;
 	if (OGLRef.selectedRenderingFBO == OGLRef.fboMSIntermediateRenderID)
 	{
 		glBindFramebuffer(GL_READ_FRAMEBUFFER, OGLRef.fboRenderID);
 		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, OGLRef.fboMSIntermediateRenderID);
 		
-		// Blit the color buffer
-		glReadBuffer(GL_COLOR_ATTACHMENT0);
-		glDrawBuffer(GL_COLOR_ATTACHMENT0);
-		glBlitFramebuffer(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
-		
 		// Blit the working depth buffer
 		glReadBuffer(GL_COLOR_ATTACHMENT1);
 		glDrawBuffer(GL_COLOR_ATTACHMENT1);
@@ -1278,17 +1268,26 @@ Render3DError OpenGLRenderer_3_2::ClearUsingImage(const u16 *__restrict colorBuf
 		glDrawBuffer(GL_COLOR_ATTACHMENT3);
 		glBlitFramebuffer(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
 		
-		// Reset framebuffer targets
+		// Blit the color buffer. Do this last so that color attachment 0 is set to the read FBO.
 		glReadBuffer(GL_COLOR_ATTACHMENT0);
-		glDrawBuffers(4, RenderDrawList);
-		glBindFramebuffer(GL_FRAMEBUFFER, OGLRef.fboMSIntermediateRenderID);
+		glDrawBuffer(GL_COLOR_ATTACHMENT0);
+		glBlitFramebuffer(0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, 0, 0, GFX3D_FRAMEBUFFER_WIDTH, GFX3D_FRAMEBUFFER_HEIGHT, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
 	}
 	
+	glBindFramebuffer(GL_FRAMEBUFFER, OGLRef.selectedRenderingFBO);
+	glDrawBuffers(4, RenderDrawList);
+	
 	return OGLERROR_NOERR;
 }
 
 Render3DError OpenGLRenderer_3_2::ClearUsingValues(const FragmentColor &clearColor, const FragmentAttributes &clearAttributes) const
 {
+	OGLRenderRef &OGLRef = *this->ref;
+	OGLRef.selectedRenderingFBO = (CommonSettings.GFX3D_Renderer_Multisample) ? OGLRef.fboMSIntermediateRenderID : OGLRef.fboRenderID;
+	glBindFramebuffer(GL_FRAMEBUFFER, OGLRef.selectedRenderingFBO);
+	glDrawBuffers(4, RenderDrawList);
+	glDepthMask(GL_TRUE);
+	
 	const GLfloat oglColor[4] = {divide5bitBy31_LUT[clearColor.r], divide5bitBy31_LUT[clearColor.g], divide5bitBy31_LUT[clearColor.b], divide5bitBy31_LUT[clearColor.a]};
 	const GLfloat oglDepth[4] = {(GLfloat)(clearAttributes.depth & 0x000000FF)/255.0f, (GLfloat)((clearAttributes.depth >> 8) & 0x000000FF)/255.0f, (GLfloat)((clearAttributes.depth >> 16) & 0x000000FF)/255.0f, 1.0};
 	const GLfloat oglPolyID[4] = {(GLfloat)clearAttributes.opaquePolyID/63.0f, 0.0, 0.0, 1.0};
@@ -1309,10 +1308,8 @@ void OpenGLRenderer_3_2::SetPolygonIndex(const size_t index)
 	glUniform1i(this->ref->uniformPolyStateIndex, index);
 }
 
-
 Render3DError OpenGLRenderer_3_2::SetupPolygon(const POLY &thePoly)
 {
-	//OGLRenderRef &OGLRef = *this->ref;
 	const PolygonAttributes attr = thePoly.getAttributes();
 	
 	// Set up depth test mode
diff --git a/desmume/src/OGLRender_3_2.h b/desmume/src/OGLRender_3_2.h
index e4ac9f8aa..45370e23e 100644
--- a/desmume/src/OGLRender_3_2.h
+++ b/desmume/src/OGLRender_3_2.h
@@ -78,9 +78,8 @@ protected:
 	virtual void DestroyGeometryProgram();
 	
 	virtual void GetExtensionSet(std::set<std::string> *oglExtensionSet);
-	virtual Render3DError EnableVertexAttributes(const VERTLIST *vertList, const GLushort *indexBuffer, const size_t vertIndexCount);
+	virtual Render3DError EnableVertexAttributes();
 	virtual Render3DError DisableVertexAttributes();
-	virtual Render3DError SelectRenderingFramebuffer();
 	virtual Render3DError DownsampleFBO();
 	virtual Render3DError BeginRender(const GFX3D &engine);
 	virtual Render3DError RenderEdgeMarking(const u16 *colorTable, const bool useAntialias);
diff --git a/desmume/src/gfx3d.h b/desmume/src/gfx3d.h
index beefdf696..1ffdd2163 100644
--- a/desmume/src/gfx3d.h
+++ b/desmume/src/gfx3d.h
@@ -485,7 +485,7 @@ struct POLY {
 	void load(EMUFILE* is);
 };
 
-#define POLYLIST_SIZE 100000
+#define POLYLIST_SIZE 20000
 struct POLYLIST {
 	POLY list[POLYLIST_SIZE];
 	int count;
@@ -553,15 +553,15 @@ struct VERT {
 	void load(EMUFILE* is);
 };
 
-#define VERTLIST_SIZE 400000
-//#define VERTLIST_SIZE 10000
+#define VERTLIST_SIZE (POLYLIST_SIZE * 4)
 struct VERTLIST {
 	VERT list[VERTLIST_SIZE];
 	int count;
 };
 
+#define INDEXLIST_SIZE (POLYLIST_SIZE * 4)
 struct INDEXLIST {
-	int list[POLYLIST_SIZE];
+	int list[INDEXLIST_SIZE];
 };
 
 
diff --git a/desmume/src/render3D.cpp b/desmume/src/render3D.cpp
index d5e5c496b..0d1117835 100644
--- a/desmume/src/render3D.cpp
+++ b/desmume/src/render3D.cpp
@@ -25,7 +25,7 @@
 #include "MMU.h"
 #include "texcache.h"
 
-static CACHE_ALIGN u32 dsDepthToD24S8_LUT[32768] = {0};
+static CACHE_ALIGN u32 dsDepthToD24_LUT[32768] = {0};
 int cur3DCore = GPU3D_NULL;
 
 GPU3DInterface gpu3DNull = { 
@@ -107,7 +107,7 @@ Render3D::Render3D()
 	{
 		for (size_t i = 0; i < 32768; i++)
 		{
-			dsDepthToD24S8_LUT[i] = (u32)DS_DEPTH15TO24(i) << 8;
+			dsDepthToD24_LUT[i] = (u32)DS_DEPTH15TO24(i);
 		}
 		
 		needTableInit = false;
@@ -195,7 +195,7 @@ Render3DError Render3D::ClearFramebuffer(const GFX3D_State &renderState)
 				
 				//this is tested quite well in the sonic chronicles main map mode
 				//where depth values are used for trees etc you can walk behind
-				this->clearImageDepthBuffer[dd] = dsDepthToD24S8_LUT[clearDepthBuffer[adr] & 0x7FFF];
+				this->clearImageDepthBuffer[dd] = dsDepthToD24_LUT[clearDepthBuffer[adr] & 0x7FFF];
 				
 				this->clearImageFogBuffer[dd] = BIT15(clearDepthBuffer[adr]);
 				this->clearImagePolyIDBuffer[dd] = clearFragment.opaquePolyID;