From 6670cacddcc6aacc17181409457c575c6539cace Mon Sep 17 00:00:00 2001
From: degasus <wickmarkus@web.de>
Date: Fri, 10 Oct 2014 00:06:29 +0200
Subject: [PATCH 01/66] use GL_TEXTURE_2D_ARRAY for most of our textures

---
 .../VideoBackends/OGL/FramebufferManager.cpp  | 72 ++++++++++---------
 .../VideoBackends/OGL/FramebufferManager.h    |  4 +-
 Source/Core/VideoBackends/OGL/Render.cpp      |  9 +++
 .../Core/VideoBackends/OGL/TextureCache.cpp   | 40 ++++++-----
 .../VideoBackends/OGL/TextureConverter.cpp    | 22 +++---
 Source/Core/VideoCommon/PixelShaderGen.cpp    |  4 +-
 .../VideoCommon/TextureConversionShader.cpp   |  4 +-
 7 files changed, 86 insertions(+), 69 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
index a8441952e9..24a2f247a9 100644
--- a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
+++ b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
@@ -21,6 +21,7 @@ int FramebufferManager::m_targetHeight;
 int FramebufferManager::m_msaaSamples;
 
 GLenum FramebufferManager::m_textureType;
+int FramebufferManager::m_EFBLayers;
 
 GLuint FramebufferManager::m_efbFramebuffer;
 GLuint FramebufferManager::m_xfbFramebuffer;
@@ -72,42 +73,45 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
 	m_efbDepth = glObj[1];
 	m_efbColorSwap = glObj[2];
 
+	m_EFBLayers = 1;
+
 	// OpenGL MSAA textures are a different kind of texture type and must be allocated
 	// with a different function, so we create them separately.
 	if (m_msaaSamples <= 1)
 	{
-		m_textureType = GL_TEXTURE_2D;
+		m_textureType = GL_TEXTURE_2D_ARRAY;
 
 		glBindTexture(m_textureType, m_efbColor);
 		glTexParameteri(m_textureType, GL_TEXTURE_MAX_LEVEL, 0);
 		glTexParameteri(m_textureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 		glTexParameteri(m_textureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-		glTexImage2D(m_textureType, 0, GL_RGBA, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+		glTexImage3D(m_textureType, 0, GL_RGBA, m_targetWidth, m_targetHeight, m_EFBLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
 
 		glBindTexture(m_textureType, m_efbDepth);
 		glTexParameteri(m_textureType, GL_TEXTURE_MAX_LEVEL, 0);
 		glTexParameteri(m_textureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 		glTexParameteri(m_textureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-		glTexImage2D(m_textureType, 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+		glTexImage3D(m_textureType, 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, m_EFBLayers, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
 
 		glBindTexture(m_textureType, m_efbColorSwap);
 		glTexParameteri(m_textureType, GL_TEXTURE_MAX_LEVEL, 0);
 		glTexParameteri(m_textureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 		glTexParameteri(m_textureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-		glTexImage2D(m_textureType, 0, GL_RGBA, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+		glTexImage3D(m_textureType, 0, GL_RGBA, m_targetWidth, m_targetHeight, m_EFBLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
 	}
 	else
 	{
-		m_textureType = GL_TEXTURE_2D_MULTISAMPLE;
+		m_textureType = GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
+		GLenum resolvedType = GL_TEXTURE_2D_ARRAY;
 
 		glBindTexture(m_textureType, m_efbColor);
-		glTexImage2DMultisample(m_textureType, m_msaaSamples, GL_RGBA, m_targetWidth, m_targetHeight, false);
+		glTexImage3DMultisample(m_textureType, m_msaaSamples, GL_RGBA, m_targetWidth, m_targetHeight, m_EFBLayers, false);
 
 		glBindTexture(m_textureType, m_efbDepth);
-		glTexImage2DMultisample(m_textureType, m_msaaSamples, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, false);
+		glTexImage3DMultisample(m_textureType, m_msaaSamples, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, m_EFBLayers, false);
 
 		glBindTexture(m_textureType, m_efbColorSwap);
-		glTexImage2DMultisample(m_textureType, m_msaaSamples, GL_RGBA, m_targetWidth, m_targetHeight, false);
+		glTexImage3DMultisample(m_textureType, m_msaaSamples, GL_RGBA, m_targetWidth, m_targetHeight, m_EFBLayers, false);
 		glBindTexture(m_textureType, 0);
 
 		// Although we are able to access the multisampled texture directly, we don't do it everywhere.
@@ -118,23 +122,23 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
 		m_resolvedColorTexture = glObj[0];
 		m_resolvedDepthTexture = glObj[1];
 
-		glBindTexture(GL_TEXTURE_2D, m_resolvedColorTexture);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+		glBindTexture(resolvedType, m_resolvedColorTexture);
+		glTexParameteri(resolvedType, GL_TEXTURE_MAX_LEVEL, 0);
+		glTexParameteri(resolvedType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(resolvedType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+		glTexImage3D(resolvedType, 0, GL_RGBA, m_targetWidth, m_targetHeight, m_EFBLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
 
-		glBindTexture(GL_TEXTURE_2D, m_resolvedDepthTexture);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+		glBindTexture(resolvedType, m_resolvedDepthTexture);
+		glTexParameteri(resolvedType, GL_TEXTURE_MAX_LEVEL, 0);
+		glTexParameteri(resolvedType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(resolvedType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+		glTexImage3D(resolvedType, 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, m_EFBLayers, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
 
 		// Bind resolved textures to resolved framebuffer.
 		glGenFramebuffers(1, &m_resolvedFramebuffer);
 		glBindFramebuffer(GL_FRAMEBUFFER, m_resolvedFramebuffer);
-		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_resolvedColorTexture, 0);
-		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_resolvedDepthTexture, 0);
+		glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_resolvedColorTexture, 0);
+		glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_resolvedDepthTexture, 0);
 	}
 
 	// Create XFB framebuffer; targets will be created elsewhere.
@@ -143,8 +147,8 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
 	// Bind target textures to EFB framebuffer.
 	glGenFramebuffers(1, &m_efbFramebuffer);
 	glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer);
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_textureType, m_efbColor, 0);
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_textureType, m_efbDepth, 0);
+	glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_efbColor, 0);
+	glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_efbDepth, 0);
 
 	// EFB framebuffer is currently bound, make sure to clear its alpha value to 1.f
 	glViewport(0, 0, m_targetWidth, m_targetHeight);
@@ -168,9 +172,9 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
 	{
 		// non-msaa, so just fetch the pixel
 		sampler =
-			"SAMPLER_BINDING(9) uniform sampler2D samp9;\n"
+			"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 			"vec4 sampleEFB(ivec2 pos) {\n"
-			"	return texelFetch(samp9, pos, 0);\n"
+			"	return texelFetch(samp9, ivec3(pos, 0), 0);\n"
 			"}\n";
 	}
 	else if (g_ogl_config.bSupportSampleShading)
@@ -179,9 +183,9 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
 		// This will lead to sample shading, but it's the only way to not loose
 		// the values of each sample.
 		sampler =
-			"SAMPLER_BINDING(9) uniform sampler2DMS samp9;\n"
+			"SAMPLER_BINDING(9) uniform sampler2DMSArray samp9;\n"
 			"vec4 sampleEFB(ivec2 pos) {\n"
-			"	return texelFetch(samp9, pos, gl_SampleID);\n"
+			"	return texelFetch(samp9, ivec3(pos, 0), gl_SampleID);\n"
 			"}\n";
 	}
 	else
@@ -190,11 +194,11 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
 		std::stringstream samples;
 		samples << m_msaaSamples;
 		sampler =
-			"SAMPLER_BINDING(9) uniform sampler2DMS samp9;\n"
+			"SAMPLER_BINDING(9) uniform sampler2DMSArray samp9;\n"
 			"vec4 sampleEFB(ivec2 pos) {\n"
 			"	vec4 color = vec4(0.0, 0.0, 0.0, 0.0);\n"
 			"	for(int i=0; i<" + samples.str() + "; i++)\n"
-			"		color += texelFetch(samp9, pos, i);\n"
+			"		color += texelFetch(samp9, ivec3(pos, 0), i);\n"
 			"	return color / " + samples.str() + ";\n"
 			"}\n";
 	}
@@ -365,7 +369,7 @@ void FramebufferManager::ReinterpretPixelData(unsigned int convtype)
 	src_texture = m_efbColor;
 	m_efbColor = m_efbColorSwap;
 	m_efbColorSwap = src_texture;
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_textureType, m_efbColor, 0);
+	glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_efbColor, 0);
 
 	glViewport(0,0, m_targetWidth, m_targetHeight);
 	glActiveTexture(GL_TEXTURE0 + 9);
@@ -397,7 +401,7 @@ void XFBSource::CopyEFB(float Gamma)
 	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FramebufferManager::GetXFBFramebuffer());
 
 	// Bind texture.
-	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+	glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);
 
 	glBlitFramebuffer(
 		0, 0, texWidth, texHeight,
@@ -419,11 +423,11 @@ XFBSourceBase* FramebufferManager::CreateXFBSource(unsigned int target_width, un
 	glGenTextures(1, &texture);
 
 	glActiveTexture(GL_TEXTURE0 + 9);
-	glBindTexture(GL_TEXTURE_2D, texture);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, target_width, target_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+	glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
+	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
+	glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, target_width, target_height, m_EFBLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
 
-	return new XFBSource(texture);
+	return new XFBSource(texture, m_EFBLayers);
 }
 
 void FramebufferManager::GetTargetSize(unsigned int *width, unsigned int *height, const EFBRectangle& sourceRc)
diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.h b/Source/Core/VideoBackends/OGL/FramebufferManager.h
index 1d0670408e..49be95a174 100644
--- a/Source/Core/VideoBackends/OGL/FramebufferManager.h
+++ b/Source/Core/VideoBackends/OGL/FramebufferManager.h
@@ -47,13 +47,14 @@ namespace OGL
 
 struct XFBSource : public XFBSourceBase
 {
-	XFBSource(GLuint tex) : texture(tex) {}
+	XFBSource(GLuint tex, int layers) : texture(tex), m_layers(layers) {}
 	~XFBSource();
 
 	void CopyEFB(float Gamma) override;
 	void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) override;
 
 	const GLuint texture;
+	const int m_layers;
 };
 
 class FramebufferManager : public FramebufferManagerBase
@@ -100,6 +101,7 @@ private:
 	static int m_msaaSamples;
 
 	static GLenum m_textureType;
+	static int m_EFBLayers;
 
 	static GLuint m_efbFramebuffer;
 	static GLuint m_xfbFramebuffer;
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 3d21b6891c..91c1b0b572 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -206,6 +206,10 @@ static void GLAPIENTRY ClearDepthf(GLfloat depthval)
 {
 	glClearDepth(depthval);
 }
+static void GLAPIENTRY FramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level)
+{
+	glFramebufferTextureLayer(target, attachment, texture, level, 0);
+}
 
 static void InitDriverInfo()
 {
@@ -462,6 +466,11 @@ Renderer::Renderer()
 		glClearDepthf = ClearDepthf;
 	}
 
+	if (!(GLExtensions::Version() >= 320))
+	{
+		glFramebufferTexture = FramebufferTexture;
+	}
+
 	g_Config.backend_info.bSupportsDualSourceBlend = GLExtensions::Supports("GL_ARB_blend_func_extended");
 	g_Config.backend_info.bSupportsPrimitiveRestart = !DriverDetails::HasBug(DriverDetails::BUG_PRIMITIVERESTART) &&
 				((GLExtensions::Version() >= 310) || GLExtensions::Supports("GL_NV_primitive_restart"));
diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index 5f14704cae..9102c3dd09 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -98,14 +98,14 @@ void TextureCache::TCacheEntry::Bind(unsigned int stage)
 			s_ActiveTexture = stage;
 		}
 
-		glBindTexture(GL_TEXTURE_2D, texture);
+		glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
 		s_Textures[stage] = texture;
 	}
 }
 
 bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int level)
 {
-	return SaveTexture(filename, GL_TEXTURE_2D, texture, virtual_width, virtual_height, level);
+	return SaveTexture(filename, GL_TEXTURE_2D_ARRAY, texture, virtual_width, virtual_height, level);
 }
 
 TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width,
@@ -172,8 +172,8 @@ TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width,
 	entry.pcfmt = pcfmt;
 
 	glActiveTexture(GL_TEXTURE0+9);
-	glBindTexture(GL_TEXTURE_2D, entry.texture);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, tex_levels - 1);
+	glBindTexture(GL_TEXTURE_2D_ARRAY, entry.texture);
+	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, tex_levels - 1);
 
 	entry.Load(width, height, expanded_width, 0);
 
@@ -189,12 +189,12 @@ void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
 	if (pcfmt != PC_TEX_FMT_DXT1)
 	{
 		glActiveTexture(GL_TEXTURE0+9);
-		glBindTexture(GL_TEXTURE_2D, texture);
+		glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
 
 		if (expanded_width != width)
 			glPixelStorei(GL_UNPACK_ROW_LENGTH, expanded_width);
 
-		glTexImage2D(GL_TEXTURE_2D, level, gl_iformat, width, height, 0, gl_format, gl_type, temp);
+		glTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_iformat, width, height, 1, 0, gl_format, gl_type, temp);
 
 		if (expanded_width != width)
 			glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
@@ -213,21 +213,23 @@ TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
 {
 	TCacheEntry *const entry = new TCacheEntry;
 	glActiveTexture(GL_TEXTURE0+9);
-	glBindTexture(GL_TEXTURE_2D, entry->texture);
+	glBindTexture(GL_TEXTURE_2D_ARRAY, entry->texture);
 
 	const GLenum
 		gl_format = GL_RGBA,
 		gl_iformat = GL_RGBA,
 		gl_type = GL_UNSIGNED_BYTE;
 
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
 
-	glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, scaled_tex_w, scaled_tex_h, 0, gl_format, gl_type, nullptr);
-	glBindTexture(GL_TEXTURE_2D, 0);
+	int layers = 1;
+
+	glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, gl_iformat, scaled_tex_w, scaled_tex_h, layers, 0, gl_format, gl_type, nullptr);
+	glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
 
 	glGenFramebuffers(1, &entry->framebuffer);
 	FramebufferManager::SetFramebuffer(entry->framebuffer);
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, entry->texture, 0);
+	glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, entry->texture, 0);
 
 	SetStage();
 
@@ -251,7 +253,7 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo
 		FramebufferManager::SetFramebuffer(framebuffer);
 
 		glActiveTexture(GL_TEXTURE0+9);
-		glBindTexture(GL_TEXTURE_2D, read_texture);
+		glBindTexture(GL_TEXTURE_2D_ARRAY, read_texture);
 
 		glViewport(0, 0, virtual_width, virtual_height);
 
@@ -309,7 +311,7 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo
 	{
 		static int count = 0;
 		SaveTexture(StringFromFormat("%sefb_frame_%i.png", File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(),
-			count++), GL_TEXTURE_2D, texture, virtual_width, virtual_height, 0);
+			count++), GL_TEXTURE_2D_ARRAY, texture, virtual_width, virtual_height, 0);
 	}
 
 	g_renderer->RestoreAPIState();
@@ -318,25 +320,25 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo
 TextureCache::TextureCache()
 {
 	const char *pColorMatrixProg =
-		"SAMPLER_BINDING(9) uniform sampler2D samp9;\n"
+		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"uniform vec4 colmat[7];\n"
 		"in vec2 uv0;\n"
 		"out vec4 ocol0;\n"
 		"\n"
 		"void main(){\n"
-		"	vec4 texcol = texture(samp9, uv0);\n"
+		"	vec4 texcol = texture(samp9, vec3(uv0, 0.0));\n"
 		"	texcol = round(texcol * colmat[5]) * colmat[6];\n"
 		"	ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];\n"
 		"}\n";
 
 	const char *pDepthMatrixProg =
-		"SAMPLER_BINDING(9) uniform sampler2D samp9;\n"
+		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"uniform vec4 colmat[5];\n"
 		"in vec2 uv0;\n"
 		"out vec4 ocol0;\n"
 		"\n"
 		"void main(){\n"
-		"	vec4 texcol = texture(samp9, uv0);\n"
+		"	vec4 texcol = texture(samp9, vec3(uv0, 0.0));\n"
 
 		// 255.99998474121 = 16777215/16777216*256
 		"	float workspace = texcol.x * 255.99998474121;\n"
@@ -364,12 +366,12 @@ TextureCache::TextureCache()
 
 	const char *VProgram =
 		"out vec2 uv0;\n"
-		"SAMPLER_BINDING(9) uniform sampler2D samp9;\n"
+		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"uniform vec4 copy_position;\n" // left, top, right, bottom
 		"void main()\n"
 		"{\n"
 		"	vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
-		"	uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0));\n"
+		"	uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy);\n"
 		"	gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
 		"}\n";
 
diff --git a/Source/Core/VideoBackends/OGL/TextureConverter.cpp b/Source/Core/VideoBackends/OGL/TextureConverter.cpp
index 0b7275eaa4..4ebaf7c1ff 100644
--- a/Source/Core/VideoBackends/OGL/TextureConverter.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureConverter.cpp
@@ -72,21 +72,21 @@ static void CreatePrograms()
 	const char *VProgramRgbToYuyv =
 		"out vec2 uv0;\n"
 		"uniform vec4 copy_position;\n" // left, top, right, bottom
-		"SAMPLER_BINDING(9) uniform sampler2D samp9;\n"
+		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"void main()\n"
 		"{\n"
 		"	vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
 		"	gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
-		"	uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0));\n"
+		"	uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy);\n"
 		"}\n";
 	const char *FProgramRgbToYuyv =
-		"SAMPLER_BINDING(9) uniform sampler2D samp9;\n"
+		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"in vec2 uv0;\n"
 		"out vec4 ocol0;\n"
 		"void main()\n"
 		"{\n"
-		"	vec3 c0 = texture(samp9, (uv0 - dFdx(uv0) * 0.25)).rgb;\n"
-		"	vec3 c1 = texture(samp9, (uv0 + dFdx(uv0) * 0.25)).rgb;\n"
+		"	vec3 c0 = texture(samp9, vec3(uv0 - dFdx(uv0) * 0.25, 0.0)).rgb;\n"
+		"	vec3 c1 = texture(samp9, vec3(uv0 + dFdx(uv0) * 0.25, 0.0)).rgb;\n"
 		"	vec3 c01 = (c0 + c1) * 0.5;\n"
 		"	vec3 y_const = vec3(0.257,0.504,0.098);\n"
 		"	vec3 u_const = vec3(-0.148,-0.291,0.439);\n"
@@ -224,17 +224,17 @@ static void EncodeToRamUsingShader(GLuint srcTexture,
 
 	// set source texture
 	glActiveTexture(GL_TEXTURE0+9);
-	glBindTexture(GL_TEXTURE_2D, srcTexture);
+	glBindTexture(GL_TEXTURE_2D_ARRAY, srcTexture);
 
 	if (linearFilter)
 	{
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 	}
 	else
 	{
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 	}
 
 	glViewport(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight);
@@ -365,7 +365,7 @@ void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTextur
 	// switch to texture converter frame buffer
 	// attach destTexture as color destination
 	FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[1]);
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destTexture, 0);
+	glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, destTexture, 0);
 
 	// activate source texture
 	// set srcAddr as data for source texture
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index c0b45fdfda..4b14fc5097 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -206,7 +206,7 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 	{
 		// Declare samplers
 		for (int i = 0; i < 8; ++i)
-			out.Write("SAMPLER_BINDING(%d) uniform sampler2D samp%d;\n", i, i);
+			out.Write("SAMPLER_BINDING(%d) uniform sampler2DArray samp%d;\n", i, i);
 	}
 	else // D3D
 	{
@@ -931,7 +931,7 @@ static inline void SampleTexture(T& out, const char *texcoords, const char *texs
 	if (ApiType == API_D3D)
 		out.Write("iround(255.0 * Tex%d.Sample(samp%d,%s.xy * " I_TEXDIMS"[%d].xy)).%s;\n", texmap,texmap, texcoords, texmap, texswap);
 	else
-		out.Write("iround(255.0 * texture(samp%d,%s.xy * " I_TEXDIMS"[%d].xy)).%s;\n", texmap, texcoords, texmap, texswap);
+		out.Write("iround(255.0 * texture(samp%d, float3(%s.xy * " I_TEXDIMS"[%d].xy, 0.0))).%s;\n", texmap, texcoords, texmap, texswap);
 }
 
 static const char *tevAlphaFuncsTable[] =
diff --git a/Source/Core/VideoCommon/TextureConversionShader.cpp b/Source/Core/VideoCommon/TextureConversionShader.cpp
index b0215e4c08..1ab973d11b 100644
--- a/Source/Core/VideoCommon/TextureConversionShader.cpp
+++ b/Source/Core/VideoCommon/TextureConversionShader.cpp
@@ -70,7 +70,7 @@ static void WriteSwizzler(char*& p, u32 format, API_TYPE ApiType)
 	if (ApiType == API_OPENGL)
 	{
 		WRITE(p, "#define samp0 samp9\n");
-		WRITE(p, "SAMPLER_BINDING(9) uniform sampler2D samp0;\n");
+		WRITE(p, "SAMPLER_BINDING(9) uniform sampler2DArray samp0;\n");
 
 		WRITE(p, "  out vec4 ocol0;\n");
 		WRITE(p, "void main()\n");
@@ -120,7 +120,7 @@ static void WriteSwizzler(char*& p, u32 format, API_TYPE ApiType)
 
 static void WriteSampleColor(char*& p, const char* colorComp, const char* dest, int xoffset, API_TYPE ApiType)
 {
-	WRITE(p, "  %s = texture(samp0, uv0 + float2(%d, 0) * sample_offset).%s;\n",
+	WRITE(p, "  %s = texture(samp0, float3(uv0 + float2(%d, 0) * sample_offset, 0.0)).%s;\n",
 		dest, xoffset, colorComp
 	);
 }

From b005f61a2e8e34e9de10183d1fe6f873f7dd9133 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 16 Oct 2014 18:52:32 +0200
Subject: [PATCH 02/66] Add geometry shader generator for stereo 3D.

---
 .../VideoBackends/OGL/FramebufferManager.cpp  |   2 +-
 .../VideoBackends/OGL/ProgramShaderCache.cpp  |  42 ++++++-
 .../VideoBackends/OGL/ProgramShaderCache.h    |  17 ++-
 Source/Core/VideoBackends/OGL/Render.cpp      |   1 +
 Source/Core/VideoBackends/OGL/Render.h        |   1 +
 Source/Core/VideoCommon/CMakeLists.txt        |   3 +-
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 109 ++++++++++++++++++
 Source/Core/VideoCommon/GeometryShaderGen.h   |  25 ++++
 .../Core/VideoCommon/VertexShaderManager.cpp  |   2 +-
 Source/Core/VideoCommon/VideoCommon.vcxproj   |   2 +
 .../VideoCommon/VideoCommon.vcxproj.filters   |   6 +
 Source/Core/VideoCommon/VideoConfig.cpp       |  18 +--
 Source/Core/VideoCommon/VideoConfig.h         |   6 +-
 13 files changed, 209 insertions(+), 25 deletions(-)
 create mode 100644 Source/Core/VideoCommon/GeometryShaderGen.cpp
 create mode 100644 Source/Core/VideoCommon/GeometryShaderGen.h

diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
index 24a2f247a9..8603c3b030 100644
--- a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
+++ b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
@@ -73,7 +73,7 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
 	m_efbDepth = glObj[1];
 	m_efbColorSwap = glObj[2];
 
-	m_EFBLayers = 1;
+	m_EFBLayers = (g_ActiveConfig.bStereo) ? 2 : 1;
 
 	// OpenGL MSAA textures are a different kind of texture type and must be allocated
 	// with a different function, so we create them separately.
diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
index f25f9c90cf..4efecef4aa 100644
--- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
+++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
@@ -36,6 +36,7 @@ ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_entry;
 SHADERUID ProgramShaderCache::last_uid;
 UidChecker<PixelShaderUid,PixelShaderCode> ProgramShaderCache::pixel_uid_checker;
 UidChecker<VertexShaderUid,VertexShaderCode> ProgramShaderCache::vertex_uid_checker;
+UidChecker<GeometryShaderUid,ShaderCode> ProgramShaderCache::geometry_uid_checker;
 
 static char s_glsl_header[1024] = "";
 
@@ -196,13 +197,17 @@ SHADER* ProgramShaderCache::SetShader(DSTALPHA_MODE dstAlphaMode, u32 components
 
 	VertexShaderCode vcode;
 	PixelShaderCode pcode;
+	ShaderCode gcode;
 	GenerateVertexShaderCode(vcode, components, API_OPENGL);
 	GeneratePixelShaderCode(pcode, dstAlphaMode, API_OPENGL, components);
+	if (g_ActiveConfig.bStereo)
+		GenerateGeometryShaderCode(gcode, components, API_OPENGL);
 
 	if (g_ActiveConfig.bEnableShaderDebugging)
 	{
 		newentry.shader.strvprog = vcode.GetBuffer();
 		newentry.shader.strpprog = pcode.GetBuffer();
+		newentry.shader.strgprog = gcode.GetBuffer();
 	}
 
 #if defined(_DEBUG) || defined(DEBUGFAST)
@@ -214,10 +219,16 @@ SHADER* ProgramShaderCache::SetShader(DSTALPHA_MODE dstAlphaMode, u32 components
 
 		filename = StringFromFormat("%sps_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
 		SaveData(filename, pcode.GetBuffer());
+
+		if (g_ActiveConfig.bStereo)
+		{
+			filename = StringFromFormat("%sgs_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
+			SaveData(filename, gcode.GetBuffer());
+		}
 	}
 #endif
 
-	if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer()))
+	if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()))
 	{
 		GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
 		return nullptr;
@@ -231,15 +242,21 @@ SHADER* ProgramShaderCache::SetShader(DSTALPHA_MODE dstAlphaMode, u32 components
 	return &last_entry->shader;
 }
 
-bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const char* pcode)
+bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const char* pcode, const char* gcode)
 {
 	GLuint vsid = CompileSingleShader(GL_VERTEX_SHADER, vcode);
 	GLuint psid = CompileSingleShader(GL_FRAGMENT_SHADER, pcode);
 
+	// Optional geometry shader
+	GLuint gsid = 0;
+	if (gcode)
+		gsid = CompileSingleShader(GL_GEOMETRY_SHADER, gcode);
+
 	if (!vsid || !psid)
 	{
 		glDeleteShader(vsid);
 		glDeleteShader(psid);
+		glDeleteShader(gsid);
 		return false;
 	}
 
@@ -247,6 +264,7 @@ bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const
 
 	glAttachShader(pid, vsid);
 	glAttachShader(pid, psid);
+	glAttachShader(pid, gsid);
 
 	if (g_ogl_config.bSupportsGLSLCache)
 		glProgramParameteri(pid, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
@@ -258,6 +276,7 @@ bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const
 	// original shaders aren't needed any more
 	glDeleteShader(vsid);
 	glDeleteShader(psid);
+	glDeleteShader(gsid);
 
 	GLint linkStatus;
 	glGetProgramiv(pid, GL_LINK_STATUS, &linkStatus);
@@ -273,7 +292,10 @@ bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const
 		std::string filename = StringFromFormat("%sbad_p_%d.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++);
 		std::ofstream file;
 		OpenFStream(file, filename, std::ios_base::out);
-		file << s_glsl_header << vcode << s_glsl_header << pcode << infoLog;
+		file << s_glsl_header << vcode << s_glsl_header << pcode;
+		if (gcode)
+			file << s_glsl_header << gcode;
+		file << infoLog;
 		file.close();
 
 		if (linkStatus != GL_TRUE)
@@ -324,11 +346,11 @@ GLuint ProgramShaderCache::CompileSingleShader(GLuint type, const char* code)
 		GLsizei charsWritten;
 		GLchar* infoLog = new GLchar[length];
 		glGetShaderInfoLog(result, length, &charsWritten, infoLog);
-		ERROR_LOG(VIDEO, "%s Shader info log:\n%s", type==GL_VERTEX_SHADER ? "VS" : "PS", infoLog);
+		ERROR_LOG(VIDEO, "%s Shader info log:\n%s", type==GL_VERTEX_SHADER ? "VS" : type==GL_FRAGMENT_SHADER ? "PS" : "GS", infoLog);
 
 		std::string filename = StringFromFormat("%sbad_%s_%04i.txt",
 			File::GetUserPath(D_DUMP_IDX).c_str(),
-			type==GL_VERTEX_SHADER ? "vs" : "ps",
+			type==GL_VERTEX_SHADER ? "vs" : type==GL_FRAGMENT_SHADER ? "ps" : "gs",
 			num_failures++);
 		std::ofstream file;
 		OpenFStream(file, filename, std::ios_base::out);
@@ -338,7 +360,7 @@ GLuint ProgramShaderCache::CompileSingleShader(GLuint type, const char* code)
 		if (compileStatus != GL_TRUE)
 		{
 			PanicAlert("Failed to compile %s shader!\nThis usually happens when trying to use Dolphin with an outdated GPU or integrated GPU like the Intel GMA series.\n\nIf you're sure this is Dolphin's error anyway, post the contents of %s along with this error message at the forums.\n\nDebug info (%s, %s, %s):\n%s",
-				type == GL_VERTEX_SHADER ? "vertex" : "pixel",
+				type == GL_VERTEX_SHADER ? "vertex" : type==GL_FRAGMENT_SHADER ? "pixel" : "geometry",
 				filename.c_str(),
 				g_ogl_config.gl_vendor,
 				g_ogl_config.gl_renderer,
@@ -365,6 +387,8 @@ void ProgramShaderCache::GetShaderId(SHADERUID* uid, DSTALPHA_MODE dstAlphaMode,
 {
 	GetPixelShaderUid(uid->puid, dstAlphaMode, API_OPENGL, components);
 	GetVertexShaderUid(uid->vuid, components, API_OPENGL);
+	if (g_ActiveConfig.bStereo)
+		GetGeometryShaderUid(uid->guid, components, API_OPENGL);
 
 	if (g_ActiveConfig.bEnableShaderDebugging)
 	{
@@ -375,6 +399,10 @@ void ProgramShaderCache::GetShaderId(SHADERUID* uid, DSTALPHA_MODE dstAlphaMode,
 		VertexShaderCode vcode;
 		GenerateVertexShaderCode(vcode, components, API_OPENGL);
 		vertex_uid_checker.AddToIndexAndCheck(vcode, uid->vuid, "Vertex", "v");
+
+		ShaderCode gcode;
+		GenerateGeometryShaderCode(gcode, components, API_OPENGL);
+		geometry_uid_checker.AddToIndexAndCheck(gcode, uid->guid, "Geometry", "g");
 	}
 }
 
@@ -486,6 +514,7 @@ void ProgramShaderCache::CreateHeader()
 		"%s\n" // sample shading
 		"%s\n" // Sampler binding
 		"%s\n" // storage buffer
+		"%s\n" // shader5
 
 		// Precision defines for GLSL ES
 		"%s\n"
@@ -518,6 +547,7 @@ void ProgramShaderCache::CreateHeader()
 		, (g_ogl_config.bSupportSampleShading) ? "#extension GL_ARB_sample_shading : enable" : ""
 		, g_ActiveConfig.backend_info.bSupportsBindingLayout ? "#define SAMPLER_BINDING(x) layout(binding = x)" : "#define SAMPLER_BINDING(x)"
 		, g_ActiveConfig.backend_info.bSupportsBBox ? "#extension GL_ARB_shader_storage_buffer_object : enable" : ""
+		, (g_ogl_config.bSupportGSInvocation) ? "#extension GL_ARB_gpu_shader5 : enable" : ""
 
 		, v>=GLSLES_300 ? "precision highp float;" : ""
 		, v>=GLSLES_300 ? "precision highp int;" : ""
diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h
index dd376142be..7286ade6ef 100644
--- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h
+++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h
@@ -9,6 +9,7 @@
 #include "VideoBackends/OGL/GLUtil.h"
 #include "VideoCommon/PixelShaderGen.h"
 #include "VideoCommon/VertexShaderGen.h"
+#include "VideoCommon/GeometryShaderGen.h"
 
 namespace OGL
 {
@@ -18,10 +19,11 @@ class SHADERUID
 public:
 	VertexShaderUid vuid;
 	PixelShaderUid puid;
+	GeometryShaderUid guid;
 
 	SHADERUID() {}
 
-	SHADERUID(const SHADERUID& r) : vuid(r.vuid), puid(r.puid) {}
+	SHADERUID(const SHADERUID& r) : vuid(r.vuid), puid(r.puid), guid(r.guid) {}
 
 	bool operator <(const SHADERUID& r) const
 	{
@@ -34,12 +36,18 @@ public:
 		if (vuid < r.vuid)
 			return true;
 
+		if (r.vuid < vuid)
+			return false;
+
+		if (guid < r.guid)
+			return true;
+
 		return false;
 	}
 
 	bool operator ==(const SHADERUID& r) const
 	{
-		return puid == r.puid && vuid == r.vuid;
+		return puid == r.puid && vuid == r.vuid && guid == r.guid;
 	}
 };
 
@@ -54,7 +62,7 @@ struct SHADER
 	}
 	GLuint glprogid; // opengl program id
 
-	std::string strvprog, strpprog;
+	std::string strvprog, strpprog, strgprog;
 
 	void SetProgramVariables();
 	void SetProgramBindings();
@@ -83,7 +91,7 @@ public:
 	static SHADER* SetShader(DSTALPHA_MODE dstAlphaMode, u32 components);
 	static void GetShaderId(SHADERUID *uid, DSTALPHA_MODE dstAlphaMode, u32 components);
 
-	static bool CompileShader(SHADER &shader, const char* vcode, const char* pcode);
+	static bool CompileShader(SHADER &shader, const char* vcode, const char* pcode, const char* gcode = nullptr);
 	static GLuint CompileSingleShader(GLuint type, const char *code);
 	static void UploadConstants();
 
@@ -104,6 +112,7 @@ private:
 
 	static UidChecker<PixelShaderUid,PixelShaderCode> pixel_uid_checker;
 	static UidChecker<VertexShaderUid,VertexShaderCode> vertex_uid_checker;
+	static UidChecker<GeometryShaderUid,ShaderCode> geometry_uid_checker;
 
 	static u32 s_ubo_buffer_size;
 	static s32 s_ubo_align;
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 91c1b0b572..99066e24ea 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -491,6 +491,7 @@ Renderer::Renderer()
 	g_ogl_config.bSupportSampleShading = GLExtensions::Supports("GL_ARB_sample_shading");
 	g_ogl_config.bSupportOGL31 = GLExtensions::Version() >= 310;
 	g_ogl_config.bSupportViewportFloat = GLExtensions::Supports("GL_ARB_viewport_array");
+	g_ogl_config.bSupportGSInvocation = GLExtensions::Supports("GL_ARB_gpu_shader5");
 
 	if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3)
 	{
diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h
index f35487a373..3888142c47 100644
--- a/Source/Core/VideoBackends/OGL/Render.h
+++ b/Source/Core/VideoBackends/OGL/Render.h
@@ -30,6 +30,7 @@ struct VideoConfig
 	GLSL_VERSION eSupportedGLSLVersion;
 	bool bSupportOGL31;
 	bool bSupportViewportFloat;
+	bool bSupportGSInvocation;
 
 	const char* gl_vendor;
 	const char* gl_renderer;
diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt
index 87b23a25ff..14067f228f 100644
--- a/Source/Core/VideoCommon/CMakeLists.txt
+++ b/Source/Core/VideoCommon/CMakeLists.txt
@@ -38,7 +38,8 @@ set(SRCS	BoundingBox.cpp
 			VideoConfig.cpp
 			VideoState.cpp
 			XFMemory.cpp
-			XFStructs.cpp)
+			XFStructs.cpp
+			GeometryShaderGen.cpp)
 set(LIBS core png)
 
 if(_M_X86)
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
new file mode 100644
index 0000000000..1da342d68a
--- /dev/null
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -0,0 +1,109 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <cmath>
+#include <locale.h>
+#ifdef __APPLE__
+	#include <xlocale.h>
+#endif
+
+#include "VideoCommon/GeometryShaderGen.h"
+#include "VideoCommon/VideoConfig.h"
+#include "VideoCommon/VertexShaderGen.h"
+#include "VideoCommon/LightingShaderGen.h"
+
+static char text[16384];
+
+template<class T>
+static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiType)
+{
+	// Non-uid template parameters will write to the dummy data (=> gets optimized out)
+	geometry_shader_uid_data dummy_data;
+	geometry_shader_uid_data* uid_data = out.template GetUidData<geometry_shader_uid_data>();
+	if (uid_data == nullptr)
+		uid_data = &dummy_data;
+
+	out.SetBuffer(text);
+	const bool is_writing_shadercode = (out.GetBuffer() != nullptr);
+#ifndef ANDROID
+	locale_t locale;
+	locale_t old_locale;
+	if (is_writing_shadercode)
+	{
+		locale = newlocale(LC_NUMERIC_MASK, "C", nullptr); // New locale for compilation
+		old_locale = uselocale(locale); // Apply the locale for this thread
+	}
+#endif
+
+	if (is_writing_shadercode)
+		text[sizeof(text) - 1] = 0x7C;  // canary
+
+	out.Write("//Geometry Shader for 3D stereoscopy\n");
+
+	if (ApiType == API_OPENGL)
+	{
+		// Insert layout parameters
+		out.Write("layout(triangles, invocations = %d) in;\n", g_ActiveConfig.bStereo ? 2 : 1);
+		out.Write("layout(triangle_strip, max_vertices = 3) out;\n");
+	}
+
+	out.Write("%s", s_lighting_struct);
+
+	// uniforms
+	/*if (ApiType == API_OPENGL)
+		out.Write("layout(std140%s) uniform VSBlock {\n", g_ActiveConfig.backend_info.bSupportsBindingLayout ? ", binding = 2" : "");
+	else
+		out.Write("cbuffer VSBlock {\n");
+	out.Write(
+		"\tfloat4 " I_POSNORMALMATRIX"[6];\n"
+		"\tfloat4 " I_PROJECTION"[4];\n"
+		"\tint4 " I_MATERIALS"[4];\n"
+		"\tLight " I_LIGHTS"[8];\n"
+		"\tfloat4 " I_TEXMATRICES"[24];\n"
+		"\tfloat4 " I_TRANSFORMMATRICES"[64];\n"
+		"\tfloat4 " I_NORMALMATRICES"[32];\n"
+		"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
+		"\tfloat4 " I_DEPTHPARAMS";\n"
+		"};\n");*/
+
+	ShaderCode code;
+	char buf[16384];
+	code.SetBuffer(buf);
+	GenerateVSOutputStructForGS(code, ApiType);
+	out.Write(code.GetBuffer());
+
+	out.Write("in VS_OUTPUT vertices[];\n");
+	out.Write("out VS_OUTPUT frag;\n");
+
+	out.Write("void main()\n{\n");
+	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
+	out.Write("\t\tfrag = vertices[i];\n");
+	out.Write("\t\tgl_Position = gl_in[i].gl_Position;\n");
+	out.Write("\t\tgl_Layer = gl_InvocationID;\n");
+	out.Write("\t\tEmitVertex();\n");
+	out.Write("\t}\n");
+	out.Write("\tEndPrimitive();\n");
+	out.Write("}\n");
+
+	if (is_writing_shadercode)
+	{
+		if (text[sizeof(text) - 1] != 0x7C)
+			PanicAlert("GeometryShader generator - buffer too small, canary has been eaten!");
+
+#ifndef ANDROID
+		uselocale(old_locale); // restore locale
+		freelocale(locale);
+#endif
+	}
+}
+
+void GetGeometryShaderUid(GeometryShaderUid& object, u32 components, API_TYPE ApiType)
+{
+	GenerateGeometryShader<GeometryShaderUid>(object, components, ApiType);
+}
+
+void GenerateGeometryShaderCode(ShaderCode& object, u32 components, API_TYPE ApiType)
+{
+	GenerateGeometryShader<ShaderCode>(object, components, ApiType);
+}
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.h b/Source/Core/VideoCommon/GeometryShaderGen.h
new file mode 100644
index 0000000000..d5ff83684b
--- /dev/null
+++ b/Source/Core/VideoCommon/GeometryShaderGen.h
@@ -0,0 +1,25 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "VideoCommon/ShaderGenCommon.h"
+#include "VideoCommon/VideoCommon.h"
+
+#pragma pack(1)
+
+struct geometry_shader_uid_data
+{
+	u32 NumValues() const { return sizeof(geometry_shader_uid_data); }
+
+	u32 components : 23;
+	u32 stereo : 1;
+};
+
+#pragma pack()
+
+typedef ShaderUid<geometry_shader_uid_data> GeometryShaderUid;
+
+void GenerateGeometryShaderCode(ShaderCode& object, u32 components, API_TYPE ApiType);
+void GetGeometryShaderUid(GeometryShaderUid& object, u32 components, API_TYPE ApiType);
diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp
index 93f969b0b5..b62e2f93e3 100644
--- a/Source/Core/VideoCommon/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexShaderManager.cpp
@@ -489,7 +489,7 @@ void VertexShaderManager::SetConstants()
 
 		PRIM_LOG("Projection: %f %f %f %f %f %f\n", rawProjection[0], rawProjection[1], rawProjection[2], rawProjection[3], rawProjection[4], rawProjection[5]);
 
-		if ((g_ActiveConfig.bFreeLook || g_ActiveConfig.bAnaglyphStereo ) && xfmem.projection.type == GX_PERSPECTIVE)
+		if (g_ActiveConfig.bFreeLook && xfmem.projection.type == GX_PERSPECTIVE)
 		{
 			Matrix44 mtxA;
 			Matrix44 mtxB;
diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj
index bda73f396c..31dc089736 100644
--- a/Source/Core/VideoCommon/VideoCommon.vcxproj
+++ b/Source/Core/VideoCommon/VideoCommon.vcxproj
@@ -60,6 +60,7 @@
     <ClCompile Include="PostProcessing.cpp" />
     <ClCompile Include="RenderBase.cpp" />
     <ClCompile Include="Statistics.cpp" />
+    <ClCompile Include="GeometryShaderGen.cpp" />
     <ClCompile Include="TextureCacheBase.cpp" />
     <ClCompile Include="TextureConversionShader.cpp" />
     <ClCompile Include="VertexLoader.cpp" />
@@ -110,6 +111,7 @@
     <ClInclude Include="RenderBase.h" />
     <ClInclude Include="ShaderGenCommon.h" />
     <ClInclude Include="Statistics.h" />
+    <ClInclude Include="GeometryShaderGen.h" />
     <ClInclude Include="TextureCacheBase.h" />
     <ClInclude Include="TextureConversionShader.h" />
     <ClInclude Include="TextureDecoder.h" />
diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters
index c662422200..f285ce35d4 100644
--- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters
+++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters
@@ -143,6 +143,9 @@
     <ClCompile Include="BoundingBox.cpp">
       <Filter>Util</Filter>
     </ClCompile>
+    <ClCompile Include="GeometryShaderGen.cpp">
+      <Filter>Shader Generators</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="CommandProcessor.h" />
@@ -275,6 +278,9 @@
     <ClInclude Include="BoundingBox.h">
       <Filter>Util</Filter>
     </ClInclude>
+    <ClInclude Include="GeometryShaderGen.h">
+      <Filter>Shader Generators</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Text Include="CMakeLists.txt" />
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index df72df9be2..5546fd2ce0 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -66,9 +66,9 @@ void VideoConfig::Load(const std::string& ini_file)
 	settings->Get("DumpEFBTarget", &bDumpEFBTarget, 0);
 	settings->Get("FreeLook", &bFreeLook, 0);
 	settings->Get("UseFFV1", &bUseFFV1, 0);
-	settings->Get("AnaglyphStereo", &bAnaglyphStereo, false);
-	settings->Get("AnaglyphStereoSeparation", &iAnaglyphStereoSeparation, 200);
-	settings->Get("AnaglyphFocalAngle", &iAnaglyphFocalAngle, 0);
+	settings->Get("Stereo", &bStereo, false);
+	settings->Get("StereoSeparation", &iStereoSeparation, 200);
+	settings->Get("StereoFocalAngle", &iStereoFocalAngle, 0);
 	settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
 	settings->Get("FastDepthCalc", &bFastDepthCalc, true);
 	settings->Get("MSAA", &iMultisampleMode, 0);
@@ -140,9 +140,9 @@ void VideoConfig::GameIniLoad()
 	CHECK_SETTING("Video_Settings", "UseRealXFB", bUseRealXFB);
 	CHECK_SETTING("Video_Settings", "SafeTextureCacheColorSamples", iSafeTextureCache_ColorSamples);
 	CHECK_SETTING("Video_Settings", "HiresTextures", bHiresTextures);
-	CHECK_SETTING("Video_Settings", "AnaglyphStereo", bAnaglyphStereo);
-	CHECK_SETTING("Video_Settings", "AnaglyphStereoSeparation", iAnaglyphStereoSeparation);
-	CHECK_SETTING("Video_Settings", "AnaglyphFocalAngle", iAnaglyphFocalAngle);
+	CHECK_SETTING("Video_Settings", "Stereo", bStereo);
+	CHECK_SETTING("Video_Settings", "StereoSeparation", iStereoSeparation);
+	CHECK_SETTING("Video_Settings", "StereoFocalAngle", iStereoFocalAngle);
 	CHECK_SETTING("Video_Settings", "EnablePixelLighting", bEnablePixelLighting);
 	CHECK_SETTING("Video_Settings", "FastDepthCalc", bFastDepthCalc);
 	CHECK_SETTING("Video_Settings", "MSAA", iMultisampleMode);
@@ -230,9 +230,9 @@ void VideoConfig::Save(const std::string& ini_file)
 	settings->Set("DumpEFBTarget", bDumpEFBTarget);
 	settings->Set("FreeLook", bFreeLook);
 	settings->Set("UseFFV1", bUseFFV1);
-	settings->Set("AnaglyphStereo", bAnaglyphStereo);
-	settings->Set("AnaglyphStereoSeparation", iAnaglyphStereoSeparation);
-	settings->Set("AnaglyphFocalAngle", iAnaglyphFocalAngle);
+	settings->Set("Stereo", bStereo);
+	settings->Set("StereoSeparation", iStereoSeparation);
+	settings->Set("StereoFocalAngle", iStereoFocalAngle);
 	settings->Set("EnablePixelLighting", bEnablePixelLighting);
 	settings->Set("FastDepthCalc", bFastDepthCalc);
 	settings->Set("ShowEFBCopyRegions", bShowEFBCopyRegions);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 44b0740728..11e4a47816 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -92,9 +92,9 @@ struct VideoConfig final
 	bool bDumpEFBTarget;
 	bool bUseFFV1;
 	bool bFreeLook;
-	bool bAnaglyphStereo;
-	int iAnaglyphStereoSeparation;
-	int iAnaglyphFocalAngle;
+	bool bStereo;
+	int iStereoSeparation;
+	int iStereoFocalAngle;
 	bool bBorderlessFullscreen;
 
 	// Hacks

From 2d8ec62bebb485c584887e24bd957ad4b08a4281 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sat, 25 Oct 2014 02:05:49 +0200
Subject: [PATCH 03/66] Pass VS_OUTPUT structs between shaders.

---
 Source/Core/VideoCommon/GeometryShaderGen.cpp |  6 +--
 Source/Core/VideoCommon/PixelShaderGen.cpp    | 32 +++++++-------
 Source/Core/VideoCommon/VertexShaderGen.cpp   | 42 ++++++-------------
 3 files changed, 34 insertions(+), 46 deletions(-)

diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index 1da342d68a..991189a27f 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -73,12 +73,12 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	GenerateVSOutputStructForGS(code, ApiType);
 	out.Write(code.GetBuffer());
 
-	out.Write("in VS_OUTPUT vertices[];\n");
-	out.Write("out VS_OUTPUT frag;\n");
+	out.Write("centroid in VS_OUTPUT v[];\n");
+	out.Write("centroid out VS_OUTPUT o;\n");
 
 	out.Write("void main()\n{\n");
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
-	out.Write("\t\tfrag = vertices[i];\n");
+	out.Write("\t\to = v[i];\n");
 	out.Write("\t\tgl_Position = gl_in[i].gl_Position;\n");
 	out.Write("\t\tgl_Layer = gl_InvocationID;\n");
 	out.Write("\t\tEmitVertex();\n");
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index 4b14fc5097..046384a923 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -16,6 +16,7 @@
 #include "VideoCommon/LightingShaderGen.h"
 #include "VideoCommon/NativeVertexFormat.h"
 #include "VideoCommon/PixelShaderGen.h"
+#include "VideoCommon/VertexShaderGen.h"
 #include "VideoCommon/VideoConfig.h"
 #include "VideoCommon/XFMemory.h"  // for texture projection mode
 
@@ -275,6 +276,12 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 		);
 	}
 
+	ShaderCode code;
+	char buf[16384];
+	code.SetBuffer(buf);
+	GenerateVSOutputStructForGS(code, ApiType);
+	out.Write(code.GetBuffer());
+
 	const bool forced_early_z = g_ActiveConfig.backend_info.bSupportsEarlyZ && bpmem.UseEarlyDepthTest() && (g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED);
 	const bool per_pixel_depth = (bpmem.ztex2.op != ZTEXTURE_DISABLE && bpmem.UseLateDepthTest()) || (!g_ActiveConfig.bFastDepthCalc && bpmem.zmode.testenable && !forced_early_z);
 
@@ -325,22 +332,27 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 		// As a workaround, we interpolate at the centroid of the coveraged pixel, which
 		// is always inside the primitive.
 		// Without MSAA, this flag is defined to have no effect.
-		out.Write("centroid in float4 colors_02;\n");
-		out.Write("centroid in float4 colors_12;\n");
+		out.Write("centroid in VS_OUTPUT o;\n");
+
+		out.Write("void main()\n{\n");
 
 		// compute window position if needed because binding semantic WPOS is not widely supported
 		// Let's set up attributes
 		for (unsigned int i = 0; i < numTexgen; ++i)
 		{
-			out.Write("centroid in float3 uv%d;\n", i);
+			out.Write("\tfloat3 uv%d = o.tex%d;\n", i, i);
 		}
-		out.Write("centroid in float4 clipPos;\n");
+		out.Write("\tfloat4 clipPos = o.clipPos;\n");
 		if (g_ActiveConfig.bEnablePixelLighting)
 		{
-			out.Write("centroid in float4 Normal;\n");
+			out.Write("\tfloat4 Normal = o.Normal;\n");
 		}
 
-		out.Write("void main()\n{\n");
+		// On Mali, global variables must be initialized as constants.
+		// This is why we initialize these variables locally instead.
+		out.Write("\tfloat4 colors_0 = o.colors_0;\n");
+		out.Write("\tfloat4 colors_1 = o.colors_1;\n");
+
 		out.Write("\tfloat4 rawpos = gl_FragCoord;\n");
 	}
 	else // D3D
@@ -370,14 +382,6 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 	          "\tint2 wrappedcoord=int2(0,0), tempcoord=int2(0,0);\n"
 	          "\tint4 tevin_a=int4(0,0,0,0),tevin_b=int4(0,0,0,0),tevin_c=int4(0,0,0,0),tevin_d=int4(0,0,0,0);\n\n"); // tev combiner inputs
 
-	if (ApiType == API_OPENGL)
-	{
-		// On Mali, global variables must be initialized as constants.
-		// This is why we initialize these variables locally instead.
-		out.Write("\tfloat4 colors_0 = colors_02;\n");
-		out.Write("\tfloat4 colors_1 = colors_12;\n");
-	}
-
 	if (g_ActiveConfig.bEnablePixelLighting)
 	{
 		out.Write("\tfloat3 _norm0 = normalize(Normal.xyz);\n\n");
diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp
index 894a18a0e2..2667445908 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.cpp
+++ b/Source/Core/VideoCommon/VertexShaderGen.cpp
@@ -131,22 +131,16 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 				out.Write("in float%d tex%d; // ATTR%d,\n", hastexmtx ? 3 : 2, i, SHADER_TEXTURE0_ATTRIB + i);
 		}
 
-		// Let's set up attributes
-		for (size_t i = 0; i < 8; ++i)
-		{
-			if (i < xfmem.numTexGen.numTexGens)
-			{
-				out.Write("centroid out  float3 uv%d;\n", i);
-			}
-		}
-		out.Write("centroid out   float4 clipPos;\n");
-		if (g_ActiveConfig.bEnablePixelLighting)
-			out.Write("centroid out   float4 Normal;\n");
 
-		out.Write("centroid out   float4 colors_02;\n");
-		out.Write("centroid out   float4 colors_12;\n");
+		if (g_ActiveConfig.bStereo)
+			out.Write("centroid out VS_OUTPUT v;\n");
+		else
+			out.Write("centroid out VS_OUTPUT o;\n");
 
 		out.Write("void main()\n{\n");
+
+		if (g_ActiveConfig.bStereo)
+			out.Write("VS_OUTPUT o;\n");
 	}
 	else // D3D
 	{
@@ -172,8 +166,9 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 		if (components & VB_HAS_POSMTXIDX)
 			out.Write("  int posmtx : BLENDINDICES,\n");
 		out.Write("  float4 rawpos : POSITION) {\n");
+
+		out.Write("VS_OUTPUT o;\n");
 	}
-	out.Write("VS_OUTPUT o;\n");
 
 	// transforms
 	if (components & VB_HAS_POSMTXIDX)
@@ -431,27 +426,16 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 
 	if (api_type == API_OPENGL)
 	{
-		// Bit ugly here
-		// TODO: Make pretty
-		// Will look better when we bind uniforms in GLSL 1.3
-		// clipPos/w needs to be done in pixel shader, not here
+		if (g_ActiveConfig.bStereo)
+			out.Write("v = o;\n");
 
-		for (unsigned int i = 0; i < xfmem.numTexGen.numTexGens; ++i)
-			out.Write(" uv%d.xyz =  o.tex%d;\n", i, i);
-		out.Write("  clipPos = o.clipPos;\n");
-
-		if (g_ActiveConfig.bEnablePixelLighting)
-			out.Write("  Normal = o.Normal;\n");
-
-		out.Write("colors_02 = o.colors_0;\n");
-		out.Write("colors_12 = o.colors_1;\n");
 		out.Write("gl_Position = o.pos;\n");
-		out.Write("}\n");
 	}
 	else // D3D
 	{
-		out.Write("return o;\n}\n");
+		out.Write("return o;\n");
 	}
+	out.Write("}\n");
 
 	if (is_writing_shadercode)
 	{

From 81e9004679b9c722f7995715f5dfbc40b18427c8 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 23 Oct 2014 23:29:15 +0200
Subject: [PATCH 04/66] VideoConfigDiag: Add Stereo 3D option.

---
 Source/Core/DolphinWX/VideoConfigDiag.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index 454fbb5cd5..574b04bde3 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -149,6 +149,7 @@ static wxString crop_desc = wxTRANSLATE("Crop the picture from 4:3 to 5:4 or fro
 static wxString ppshader_desc = wxTRANSLATE("Apply a post-processing effect after finishing a frame.\n\nIf unsure, select (off).");
 static wxString cache_efb_copies_desc = wxTRANSLATE("Slightly speeds up EFB to RAM copies by sacrificing emulation accuracy.\nSometimes also increases visual quality.\nIf you're experiencing any issues, try raising texture cache accuracy or disable this option.\n\nIf unsure, leave this unchecked.");
 static wxString shader_errors_desc = wxTRANSLATE("Usually if shader compilation fails, an error message is displayed.\nHowever, one may skip the popups to allow interruption free gameplay by checking this option.\n\nIf unsure, leave this unchecked.");
+static wxString stereo_3d_desc = wxTRANSLATE("Side-by-side stereoscopic 3D.");
 
 
 #if !defined(__APPLE__)
@@ -433,6 +434,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 
 	szr_enh->Add(CreateCheckBox(page_enh, _("Widescreen Hack"), wxGetTranslation(ws_hack_desc), vconfig.bWidescreenHack));
 	szr_enh->Add(CreateCheckBox(page_enh, _("Disable Fog"), wxGetTranslation(disable_fog_desc), vconfig.bDisableFog));
+	szr_enh->Add(CreateCheckBox(page_enh, _("Stereo 3D"), wxGetTranslation(stereo_3d_desc), vconfig.bStereo));
 
 	wxStaticBoxSizer* const group_enh = new wxStaticBoxSizer(wxVERTICAL, page_enh, _("Enhancements"));
 	group_enh->Add(szr_enh, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);

From c64486075d728b3682b77ed06d5c154fd4a5c88d Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Wed, 22 Oct 2014 13:02:28 +0200
Subject: [PATCH 05/66] PostProcessing: Add layered stereoscopy support.

---
 .../Core/VideoBackends/OGL/PostProcessing.cpp | 24 ++++++++-------
 .../Core/VideoBackends/OGL/PostProcessing.h   |  3 +-
 Source/Core/VideoBackends/OGL/Render.cpp      | 30 +++++++++++++++++--
 Source/Core/VideoCommon/PostProcessing.h      |  2 +-
 4 files changed, 44 insertions(+), 15 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.cpp b/Source/Core/VideoBackends/OGL/PostProcessing.cpp
index 434a1d94a3..2681fcd345 100644
--- a/Source/Core/VideoBackends/OGL/PostProcessing.cpp
+++ b/Source/Core/VideoBackends/OGL/PostProcessing.cpp
@@ -36,7 +36,7 @@ static char s_vertex_shader[] =
 	"	uv0 = rawpos * src_rect.zw + src_rect.xy;\n"
 	"}\n";
 
-OpenGLPostProcessing::OpenGLPostProcessing()
+OpenGLPostProcessing::OpenGLPostProcessing() : m_initialized(false)
 {
 	CreateHeader();
 
@@ -46,8 +46,6 @@ OpenGLPostProcessing::OpenGLPostProcessing()
 		glGenBuffers(1, &m_attribute_vbo);
 		glGenVertexArrays(1, &m_attribute_vao);
 	}
-
-	m_initialized = false;
 }
 
 OpenGLPostProcessing::~OpenGLPostProcessing()
@@ -62,7 +60,7 @@ OpenGLPostProcessing::~OpenGLPostProcessing()
 }
 
 void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle dst,
-                                           int src_texture, int src_width, int src_height)
+                                           int src_texture, int src_width, int src_height, int layer)
 {
 	ApplyShader();
 
@@ -79,6 +77,7 @@ void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle
 	glUniform4f(m_uniform_src_rect, src.left / (float) src_width, src.bottom / (float) src_height,
 		    src.GetWidth() / (float) src_width, src.GetHeight() / (float) src_height);
 	glUniform1ui(m_uniform_time, (GLuint)m_timer.GetTimeElapsed());
+	glUniform1i(m_uniform_layer, layer);
 
 	if (m_config.IsDirty())
 	{
@@ -151,9 +150,9 @@ void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle
 	}
 
 	glActiveTexture(GL_TEXTURE0+9);
-	glBindTexture(GL_TEXTURE_2D, src_texture);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glBindTexture(GL_TEXTURE_2D_ARRAY, src_texture);
+	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 }
 
@@ -167,7 +166,7 @@ void OpenGLPostProcessing::ApplyShader()
 	m_uniform_bindings.clear();
 
 	// load shader from disk
-	std::string default_shader = "void main() { SetOutput(Sample()); }";
+	std::string default_shader = "void main() { SetOutput(Sample()); }\n";
 	std::string code = "";
 	if (g_ActiveConfig.sPostProcessingShader != "")
 		code = m_config.LoadShader();
@@ -195,6 +194,7 @@ void OpenGLPostProcessing::ApplyShader()
 	m_uniform_resolution = glGetUniformLocation(m_shader.glprogid, "resolution");
 	m_uniform_time = glGetUniformLocation(m_shader.glprogid, "time");
 	m_uniform_src_rect = glGetUniformLocation(m_shader.glprogid, "src_rect");
+	m_uniform_layer = glGetUniformLocation(m_shader.glprogid, "layer");
 
 	if (m_attribute_workaround)
 	{
@@ -228,7 +228,7 @@ void OpenGLPostProcessing::CreateHeader()
 		// Shouldn't be accessed directly by the PP shader
 		// Texture sampler
 		"SAMPLER_BINDING(8) uniform sampler2D samp8;\n"
-		"SAMPLER_BINDING(9) uniform sampler2D samp9;\n"
+		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 
 		// Output variable
 		"out float4 ocol0;\n"
@@ -238,16 +238,18 @@ void OpenGLPostProcessing::CreateHeader()
 		"uniform float4 resolution;\n"
 		// Time
 		"uniform uint time;\n"
+		// Layer
+		"uniform int layer;\n"
 
 		// Interfacing functions
 		"float4 Sample()\n"
 		"{\n"
-			"\treturn texture(samp9, uv0);\n"
+			"\treturn texture(samp9, float3(uv0, layer));\n"
 		"}\n"
 
 		"float4 SampleLocation(float2 location)\n"
 		"{\n"
-			"\treturn texture(samp9, location);\n"
+			"\treturn texture(samp9, float3(location, layer));\n"
 		"}\n"
 
 		"#define SampleOffset(offset) textureOffset(samp9, uv0, offset)\n"
diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.h b/Source/Core/VideoBackends/OGL/PostProcessing.h
index 9f4f6ba5bc..4c67eca839 100644
--- a/Source/Core/VideoBackends/OGL/PostProcessing.h
+++ b/Source/Core/VideoBackends/OGL/PostProcessing.h
@@ -23,7 +23,7 @@ public:
 	~OpenGLPostProcessing();
 
 	void BlitFromTexture(TargetRectangle src, TargetRectangle dst,
-	                     int src_texture, int src_width, int src_height) override;
+	                     int src_texture, int src_width, int src_height, int layer) override;
 	void ApplyShader() override;
 
 private:
@@ -32,6 +32,7 @@ private:
 	GLuint m_uniform_resolution;
 	GLuint m_uniform_src_rect;
 	GLuint m_uniform_time;
+	GLuint m_uniform_layer;
 	std::string m_glsl_header;
 
 	// These are only used when working around Qualcomm's broken attributeless rendering
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 99066e24ea..b6c288bf2c 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -1505,7 +1505,20 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 
 			sourceRc.right -= fbStride - fbWidth;
 
-			m_post_processor->BlitFromTexture(sourceRc, drawRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
+			if (g_ActiveConfig.bStereo)
+			{
+				TargetRectangle leftRc = drawRc, rightRc = drawRc;
+				int width = drawRc.right - drawRc.left;
+				leftRc.right -= width / 2;
+				rightRc.left += width / 2;
+
+				m_post_processor->BlitFromTexture(sourceRc, leftRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 0);
+				m_post_processor->BlitFromTexture(sourceRc, rightRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 1);
+			}
+			else
+			{
+				m_post_processor->BlitFromTexture(sourceRc, drawRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 1);
+			}
 		}
 	}
 	else
@@ -1515,7 +1528,20 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 		// for msaa mode, we must resolve the efb content to non-msaa
 		GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(rc);
 
-		m_post_processor->BlitFromTexture(targetRc, flipped_trc, tex, s_target_width, s_target_height);
+		if (g_ActiveConfig.bStereo)
+		{
+			TargetRectangle leftRc = flipped_trc, rightRc = flipped_trc;
+			int width = flipped_trc.right - flipped_trc.left;
+			leftRc.right -= width / 2;
+			rightRc.left += width / 2;
+
+			m_post_processor->BlitFromTexture(targetRc, leftRc, tex, s_target_width, s_target_height, 0);
+			m_post_processor->BlitFromTexture(targetRc, rightRc, tex, s_target_width, s_target_height, 1);
+		}
+		else
+		{
+			m_post_processor->BlitFromTexture(targetRc, flipped_trc, tex, s_target_width, s_target_height);
+		}
 	}
 
 	glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
diff --git a/Source/Core/VideoCommon/PostProcessing.h b/Source/Core/VideoCommon/PostProcessing.h
index 68ab47cff6..7c4f942214 100644
--- a/Source/Core/VideoCommon/PostProcessing.h
+++ b/Source/Core/VideoCommon/PostProcessing.h
@@ -91,7 +91,7 @@ public:
 
 	// Should be implemented by the backends for backend specific code
 	virtual void BlitFromTexture(TargetRectangle src, TargetRectangle dst,
-	                             int src_texture, int src_width, int src_height) = 0;
+	                             int src_texture, int src_width, int src_height, int layer = 0) = 0;
 	virtual void ApplyShader() = 0;
 
 protected:

From f6ea293027e553cf89f780ec2a1b716cc026a4b5 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sun, 26 Oct 2014 19:17:00 +0100
Subject: [PATCH 06/66] VertexShaderManager: Compute stereoscopy projection
 matrices.

---
 Source/Core/Common/MathUtil.cpp               |  7 ++++++
 Source/Core/Common/MathUtil.h                 |  1 +
 Source/Core/VideoCommon/ConstantManager.h     |  1 +
 Source/Core/VideoCommon/GeometryShaderGen.cpp |  8 ++++---
 Source/Core/VideoCommon/PixelShaderGen.cpp    |  1 +
 Source/Core/VideoCommon/ShaderGenCommon.h     |  1 +
 Source/Core/VideoCommon/VertexShaderGen.cpp   |  7 ++++++
 .../Core/VideoCommon/VertexShaderManager.cpp  | 24 +++++++++++++++++++
 Source/Core/VideoCommon/VideoConfig.cpp       |  8 +++----
 Source/Core/VideoCommon/VideoConfig.h         |  2 +-
 10 files changed, 52 insertions(+), 8 deletions(-)

diff --git a/Source/Core/Common/MathUtil.cpp b/Source/Core/Common/MathUtil.cpp
index e409e3fe24..05822dac77 100644
--- a/Source/Core/Common/MathUtil.cpp
+++ b/Source/Core/Common/MathUtil.cpp
@@ -351,6 +351,13 @@ void Matrix44::Translate(Matrix44 &mtx, const float vec[3])
 	mtx.data[11] = vec[2];
 }
 
+void Matrix44::Shear(Matrix44 &mtx, const float a, const float b)
+{
+	LoadIdentity(mtx);
+	mtx.data[2] = a;
+	mtx.data[6] = b;
+}
+
 void Matrix44::Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result)
 {
 	MatrixMul(4, a.data, b.data, result.data);
diff --git a/Source/Core/Common/MathUtil.h b/Source/Core/Common/MathUtil.h
index 5af6ce40f9..13ef25e647 100644
--- a/Source/Core/Common/MathUtil.h
+++ b/Source/Core/Common/MathUtil.h
@@ -232,6 +232,7 @@ public:
 	static void Set(Matrix44 &mtx, const float mtxArray[16]);
 
 	static void Translate(Matrix44 &mtx, const float vec[3]);
+	static void Shear(Matrix44 &mtx, const float a, const float b = 0);
 
 	static void Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result);
 
diff --git a/Source/Core/VideoCommon/ConstantManager.h b/Source/Core/VideoCommon/ConstantManager.h
index 3e52640ca9..1c3ad55778 100644
--- a/Source/Core/VideoCommon/ConstantManager.h
+++ b/Source/Core/VideoCommon/ConstantManager.h
@@ -43,5 +43,6 @@ struct VertexShaderConstants
 	float4 normalmatrices[32];
 	float4 posttransformmatrices[64];
 	float4 depthparams;
+	float4 stereoprojection[8];
 };
 
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index 991189a27f..9f1b5b69b4 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -51,7 +51,7 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("%s", s_lighting_struct);
 
 	// uniforms
-	/*if (ApiType == API_OPENGL)
+	if (ApiType == API_OPENGL)
 		out.Write("layout(std140%s) uniform VSBlock {\n", g_ActiveConfig.backend_info.bSupportsBindingLayout ? ", binding = 2" : "");
 	else
 		out.Write("cbuffer VSBlock {\n");
@@ -65,7 +65,8 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 		"\tfloat4 " I_NORMALMATRICES"[32];\n"
 		"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
 		"\tfloat4 " I_DEPTHPARAMS";\n"
-		"};\n");*/
+		"\tfloat4 " I_STEREOPROJECTION"[8];\n"
+		"};\n");
 
 	ShaderCode code;
 	char buf[16384];
@@ -79,7 +80,8 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("void main()\n{\n");
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
 	out.Write("\t\to = v[i];\n");
-	out.Write("\t\tgl_Position = gl_in[i].gl_Position;\n");
+	out.Write("\t\to.pos = float4(dot(" I_STEREOPROJECTION"[gl_InvocationID * 4 + 0], v[i].rawpos), dot(" I_STEREOPROJECTION"[gl_InvocationID * 4 + 1], v[i].rawpos), dot(" I_STEREOPROJECTION"[gl_InvocationID * 4 + 2], v[i].rawpos), dot(" I_STEREOPROJECTION"[gl_InvocationID * 4 + 3], v[i].rawpos)); \n");
+	out.Write("\t\tgl_Position = o.pos;\n");
 	out.Write("\t\tgl_Layer = gl_InvocationID;\n");
 	out.Write("\t\tEmitVertex();\n");
 	out.Write("\t}\n");
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index 046384a923..bc98377049 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -264,6 +264,7 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 			"\tfloat4 " I_NORMALMATRICES"[32];\n"
 			"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
 			"\tfloat4 " I_DEPTHPARAMS";\n"
+			"\tfloat4 " I_STEREOPROJECTION"[8];\n"
 			"};\n");
 	}
 
diff --git a/Source/Core/VideoCommon/ShaderGenCommon.h b/Source/Core/VideoCommon/ShaderGenCommon.h
index 54adb1ba17..b395b5e81f 100644
--- a/Source/Core/VideoCommon/ShaderGenCommon.h
+++ b/Source/Core/VideoCommon/ShaderGenCommon.h
@@ -239,3 +239,4 @@ private:
 #define I_NORMALMATRICES        "cnmtx"
 #define I_POSTTRANSFORMMATRICES "cpostmtx"
 #define I_DEPTHPARAMS           "cDepth" // farZ, zRange
+#define I_STEREOPROJECTION      "csproj"
diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp
index 2667445908..d7fd94ddec 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.cpp
+++ b/Source/Core/VideoCommon/VertexShaderGen.cpp
@@ -52,6 +52,9 @@ static inline void GenerateVSOutputStruct(T& object, API_TYPE api_type)
 	if (g_ActiveConfig.bEnablePixelLighting)
 		DefineVSOutputStructMember(object, api_type, "float4", "Normal", -1, "TEXCOORD", xfmem.numTexGen.numTexGens + 1);
 
+	if (g_ActiveConfig.bStereo)
+		DefineVSOutputStructMember(object, api_type, "float4", "rawpos", -1, "POSITION");
+
 	object.Write("};\n");
 }
 
@@ -99,6 +102,7 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 		"\tfloat4 " I_NORMALMATRICES"[32];\n"
 		"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
 		"\tfloat4 " I_DEPTHPARAMS";\n"
+		"\tfloat4 " I_STEREOPROJECTION"[8];\n"
 		"};\n");
 
 	GenerateVSOutputStruct(out, api_type);
@@ -215,6 +219,9 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 
 	out.Write("o.pos = float4(dot(" I_PROJECTION"[0], pos), dot(" I_PROJECTION"[1], pos), dot(" I_PROJECTION"[2], pos), dot(" I_PROJECTION"[3], pos));\n");
 
+	if (g_ActiveConfig.bStereo)
+		out.Write("o.rawpos = pos;\n");
+
 	out.Write("int4 lacc;\n"
 			"float3 ldir, h;\n"
 			"float dist, dist2, attn;\n");
diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp
index b62e2f93e3..e40eb92152 100644
--- a/Source/Core/VideoCommon/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexShaderManager.cpp
@@ -512,6 +512,30 @@ void VertexShaderManager::SetConstants()
 			Matrix44::Multiply(s_viewportCorrection, projMtx, correctedMtx);
 			memcpy(constants.projection, correctedMtx.data, 4*16);
 		}
+
+		if (g_ActiveConfig.bStereo && xfmem.projection.type == GX_PERSPECTIVE)
+		{
+			Matrix44 projMtx;
+			Matrix44::Set(projMtx, g_fProjectionMatrix);
+
+			Matrix44 leftShearMtx, rightShearMtx;
+			Matrix44::Shear(leftShearMtx, g_ActiveConfig.iStereoSeparation / (200.0f * g_ActiveConfig.iStereoFocalLength));
+			Matrix44::Shear(rightShearMtx, -g_ActiveConfig.iStereoSeparation / (200.0f * g_ActiveConfig.iStereoFocalLength));
+
+			Matrix44 leftProjMtx, rightProjMtx, leftCorrectedMtx, rightCorrectedMtx;
+			Matrix44::Multiply(projMtx, leftShearMtx, leftProjMtx);
+			Matrix44::Multiply(s_viewportCorrection, leftProjMtx, leftCorrectedMtx);
+			Matrix44::Multiply(projMtx, rightShearMtx, rightProjMtx);
+			Matrix44::Multiply(s_viewportCorrection, rightProjMtx, rightCorrectedMtx);
+			memcpy(constants.stereoprojection, leftCorrectedMtx.data, 4*16);
+			memcpy(constants.stereoprojection + 4, rightCorrectedMtx.data, 4*16);
+		}
+		else
+		{
+			memcpy(constants.stereoprojection, constants.projection, 4 * 16);
+			memcpy(constants.stereoprojection + 4, constants.projection, 4 * 16);
+		}
+
 		dirty = true;
 	}
 }
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index 5546fd2ce0..9962160486 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -67,8 +67,8 @@ void VideoConfig::Load(const std::string& ini_file)
 	settings->Get("FreeLook", &bFreeLook, 0);
 	settings->Get("UseFFV1", &bUseFFV1, 0);
 	settings->Get("Stereo", &bStereo, false);
-	settings->Get("StereoSeparation", &iStereoSeparation, 200);
-	settings->Get("StereoFocalAngle", &iStereoFocalAngle, 0);
+	settings->Get("StereoSeparation", &iStereoSeparation, 65);
+	settings->Get("StereoFocalLength", &iStereoFocalLength, 100);
 	settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
 	settings->Get("FastDepthCalc", &bFastDepthCalc, true);
 	settings->Get("MSAA", &iMultisampleMode, 0);
@@ -142,7 +142,7 @@ void VideoConfig::GameIniLoad()
 	CHECK_SETTING("Video_Settings", "HiresTextures", bHiresTextures);
 	CHECK_SETTING("Video_Settings", "Stereo", bStereo);
 	CHECK_SETTING("Video_Settings", "StereoSeparation", iStereoSeparation);
-	CHECK_SETTING("Video_Settings", "StereoFocalAngle", iStereoFocalAngle);
+	CHECK_SETTING("Video_Settings", "StereoFocalLength", iStereoFocalLength);
 	CHECK_SETTING("Video_Settings", "EnablePixelLighting", bEnablePixelLighting);
 	CHECK_SETTING("Video_Settings", "FastDepthCalc", bFastDepthCalc);
 	CHECK_SETTING("Video_Settings", "MSAA", iMultisampleMode);
@@ -232,7 +232,7 @@ void VideoConfig::Save(const std::string& ini_file)
 	settings->Set("UseFFV1", bUseFFV1);
 	settings->Set("Stereo", bStereo);
 	settings->Set("StereoSeparation", iStereoSeparation);
-	settings->Set("StereoFocalAngle", iStereoFocalAngle);
+	settings->Set("StereoFocalLength", iStereoFocalLength);
 	settings->Set("EnablePixelLighting", bEnablePixelLighting);
 	settings->Set("FastDepthCalc", bFastDepthCalc);
 	settings->Set("ShowEFBCopyRegions", bShowEFBCopyRegions);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 11e4a47816..8f869dc100 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -94,7 +94,7 @@ struct VideoConfig final
 	bool bFreeLook;
 	bool bStereo;
 	int iStereoSeparation;
-	int iStereoFocalAngle;
+	int iStereoFocalLength;
 	bool bBorderlessFullscreen;
 
 	// Hacks

From d9e280e338b9b05d3651a5fe53d933999310aa7c Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Mon, 27 Oct 2014 00:29:37 +0100
Subject: [PATCH 07/66] PixelShaderGen: Sample the correct texture layer.

---
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 7 +++++--
 Source/Core/VideoCommon/PixelShaderGen.cpp    | 5 ++++-
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index 9f1b5b69b4..c8467b9f4c 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -77,12 +77,15 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("centroid in VS_OUTPUT v[];\n");
 	out.Write("centroid out VS_OUTPUT o;\n");
 
+	out.Write("flat out int eye;\n");
+
 	out.Write("void main()\n{\n");
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
 	out.Write("\t\to = v[i];\n");
-	out.Write("\t\to.pos = float4(dot(" I_STEREOPROJECTION"[gl_InvocationID * 4 + 0], v[i].rawpos), dot(" I_STEREOPROJECTION"[gl_InvocationID * 4 + 1], v[i].rawpos), dot(" I_STEREOPROJECTION"[gl_InvocationID * 4 + 2], v[i].rawpos), dot(" I_STEREOPROJECTION"[gl_InvocationID * 4 + 3], v[i].rawpos)); \n");
+	out.Write("\t\teye = gl_InvocationID;\n");
+	out.Write("\t\to.pos = float4(dot(" I_STEREOPROJECTION"[eye * 4 + 0], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 1], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 2], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 3], v[i].rawpos)); \n");
 	out.Write("\t\tgl_Position = o.pos;\n");
-	out.Write("\t\tgl_Layer = gl_InvocationID;\n");
+	out.Write("\t\tgl_Layer = eye;\n");
 	out.Write("\t\tEmitVertex();\n");
 	out.Write("\t}\n");
 	out.Write("\tEndPrimitive();\n");
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index bc98377049..edad01ee77 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -335,6 +335,9 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 		// Without MSAA, this flag is defined to have no effect.
 		out.Write("centroid in VS_OUTPUT o;\n");
 
+		if (g_ActiveConfig.bStereo)
+			out.Write("flat in int eye;\n");
+
 		out.Write("void main()\n{\n");
 
 		// compute window position if needed because binding semantic WPOS is not widely supported
@@ -936,7 +939,7 @@ static inline void SampleTexture(T& out, const char *texcoords, const char *texs
 	if (ApiType == API_D3D)
 		out.Write("iround(255.0 * Tex%d.Sample(samp%d,%s.xy * " I_TEXDIMS"[%d].xy)).%s;\n", texmap,texmap, texcoords, texmap, texswap);
 	else
-		out.Write("iround(255.0 * texture(samp%d, float3(%s.xy * " I_TEXDIMS"[%d].xy, 0.0))).%s;\n", texmap, texcoords, texmap, texswap);
+		out.Write("iround(255.0 * texture(samp%d, float3(%s.xy * " I_TEXDIMS"[%d].xy, %s))).%s;\n", texmap, texcoords, texmap, g_ActiveConfig.bStereo ? "eye" : "0.0", texswap);
 }
 
 static const char *tevAlphaFuncsTable[] =

From 80616c6e9e8b0c5f622793221a2ad1f8fdc24084 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Mon, 27 Oct 2014 00:52:56 +0100
Subject: [PATCH 08/66] TextureCache: Implement layered framebuffer support.

Stereoscopic EFB2Tex is now supported.
---
 .../VideoBackends/OGL/FramebufferManager.h    |  2 +
 .../Core/VideoBackends/OGL/TextureCache.cpp   | 40 ++++++++++++++-----
 2 files changed, 31 insertions(+), 11 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.h b/Source/Core/VideoBackends/OGL/FramebufferManager.h
index 49be95a174..bce63a0048 100644
--- a/Source/Core/VideoBackends/OGL/FramebufferManager.h
+++ b/Source/Core/VideoBackends/OGL/FramebufferManager.h
@@ -74,6 +74,8 @@ public:
 	// Resolved framebuffer is only used in MSAA mode.
 	static GLuint GetResolvedFramebuffer() { return m_resolvedFramebuffer; }
 
+	static int GetEFBLayers() { return m_EFBLayers; }
+
 	static void SetFramebuffer(GLuint fb);
 
 	// If in MSAA mode, this will perform a resolve of the specified rectangle, and return the resolve target as a texture ID.
diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index 9102c3dd09..fbf3660ba0 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -222,9 +222,7 @@ TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
 
 	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
 
-	int layers = 1;
-
-	glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, gl_iformat, scaled_tex_w, scaled_tex_h, layers, 0, gl_format, gl_type, nullptr);
+	glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, gl_iformat, scaled_tex_w, scaled_tex_h, FramebufferManager::GetEFBLayers(), 0, gl_format, gl_type, nullptr);
 	glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
 
 	glGenFramebuffers(1, &entry->framebuffer);
@@ -322,11 +320,11 @@ TextureCache::TextureCache()
 	const char *pColorMatrixProg =
 		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"uniform vec4 colmat[7];\n"
-		"in vec2 uv0;\n"
+		"in vec3 f_uv0;\n"
 		"out vec4 ocol0;\n"
 		"\n"
 		"void main(){\n"
-		"	vec4 texcol = texture(samp9, vec3(uv0, 0.0));\n"
+		"	vec4 texcol = texture(samp9, f_uv0);\n"
 		"	texcol = round(texcol * colmat[5]) * colmat[6];\n"
 		"	ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];\n"
 		"}\n";
@@ -334,11 +332,11 @@ TextureCache::TextureCache()
 	const char *pDepthMatrixProg =
 		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"uniform vec4 colmat[5];\n"
-		"in vec2 uv0;\n"
+		"in vec3 f_uv0;\n"
 		"out vec4 ocol0;\n"
 		"\n"
 		"void main(){\n"
-		"	vec4 texcol = texture(samp9, vec3(uv0, 0.0));\n"
+		"	vec4 texcol = texture(samp9, f_uv0);\n"
 
 		// 255.99998474121 = 16777215/16777216*256
 		"	float workspace = texcol.x * 255.99998474121;\n"
@@ -365,18 +363,38 @@ TextureCache::TextureCache()
 		"}\n";
 
 	const char *VProgram =
-		"out vec2 uv0;\n"
+		"out vec2 v_uv0;\n"
 		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"uniform vec4 copy_position;\n" // left, top, right, bottom
 		"void main()\n"
 		"{\n"
 		"	vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
-		"	uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy);\n"
+		"	v_uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy);\n"
 		"	gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
 		"}\n";
 
-	ProgramShaderCache::CompileShader(s_ColorMatrixProgram, VProgram, pColorMatrixProg);
-	ProgramShaderCache::CompileShader(s_DepthMatrixProgram, VProgram, pDepthMatrixProg);
+	const char *GProgram =
+		"layout(triangles) in;\n"
+		"layout(triangle_strip, max_vertices = 3) out;\n"
+		"in vec2 v_uv0[];\n"
+		"out vec3 f_uv0;\n"
+		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
+		"void main()\n"
+		"{\n"
+		"	int layers = textureSize(samp9, 0).z;\n"
+		"	for (int layer = 0; layer < layers; ++layer) {\n"
+		"		for (int i = 0; i < gl_in.length(); ++i) {\n"
+		"			f_uv0 = vec3(v_uv0[i], layer);\n"
+		"			gl_Position = gl_in[i].gl_Position;\n"
+		"			gl_Layer = layer;\n"
+		"			EmitVertex();\n"
+		"		}\n"
+		"		EndPrimitive();\n"
+		"	}\n"
+		"}\n";
+
+	ProgramShaderCache::CompileShader(s_ColorMatrixProgram, VProgram, pColorMatrixProg, GProgram);
+	ProgramShaderCache::CompileShader(s_DepthMatrixProgram, VProgram, pDepthMatrixProg, GProgram);
 
 	s_ColorMatrixUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "colmat");
 	s_DepthMatrixUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "colmat");

From 272ea90ca5e7a7f2c6e9a101b7b9e899b2757a70 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Mon, 27 Oct 2014 17:38:22 +0100
Subject: [PATCH 09/66] GeometryShaderGen: Allow stereoscopy to be disabled.

Will facilitate future use of this generator for other purposes.
---
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 4 +++-
 Source/Core/VideoCommon/GeometryShaderGen.h   | 1 -
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index c8467b9f4c..e305551cd8 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -44,6 +44,7 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	if (ApiType == API_OPENGL)
 	{
 		// Insert layout parameters
+		uid_data->stereo = g_ActiveConfig.bStereo;
 		out.Write("layout(triangles, invocations = %d) in;\n", g_ActiveConfig.bStereo ? 2 : 1);
 		out.Write("layout(triangle_strip, max_vertices = 3) out;\n");
 	}
@@ -83,7 +84,8 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
 	out.Write("\t\to = v[i];\n");
 	out.Write("\t\teye = gl_InvocationID;\n");
-	out.Write("\t\to.pos = float4(dot(" I_STEREOPROJECTION"[eye * 4 + 0], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 1], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 2], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 3], v[i].rawpos)); \n");
+	if (g_ActiveConfig.bStereo)
+		out.Write("\t\to.pos = float4(dot(" I_STEREOPROJECTION"[eye * 4 + 0], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 1], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 2], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 3], v[i].rawpos)); \n");
 	out.Write("\t\tgl_Position = o.pos;\n");
 	out.Write("\t\tgl_Layer = eye;\n");
 	out.Write("\t\tEmitVertex();\n");
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.h b/Source/Core/VideoCommon/GeometryShaderGen.h
index d5ff83684b..fffa8b34ce 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.h
+++ b/Source/Core/VideoCommon/GeometryShaderGen.h
@@ -13,7 +13,6 @@ struct geometry_shader_uid_data
 {
 	u32 NumValues() const { return sizeof(geometry_shader_uid_data); }
 
-	u32 components : 23;
 	u32 stereo : 1;
 };
 

From 5944d15021e39f2356f7fbe1b6a935426bc0bb8a Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 28 Oct 2014 14:47:13 +0100
Subject: [PATCH 10/66] TextureCache: Check the number of layers before reusing
 a texture.

---
 Source/Core/VideoBackends/OGL/TextureCache.cpp | 4 +++-
 Source/Core/VideoCommon/TextureCacheBase.cpp   | 6 ++++--
 Source/Core/VideoCommon/TextureCacheBase.h     | 1 +
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index fbf3660ba0..dce92c19d7 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -222,7 +222,9 @@ TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
 
 	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
 
-	glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, gl_iformat, scaled_tex_w, scaled_tex_h, FramebufferManager::GetEFBLayers(), 0, gl_format, gl_type, nullptr);
+	entry->num_layers = FramebufferManager::GetEFBLayers();
+
+	glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, gl_iformat, scaled_tex_w, scaled_tex_h, entry->num_layers, 0, gl_format, gl_type, nullptr);
 	glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
 
 	glGenFramebuffers(1, &entry->framebuffer);
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index 0618b3f1b9..d4296cc2aa 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -444,14 +444,15 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
 		//
 		// TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies?
 		// TODO: Actually, it should be enough if the internal texture format matches...
-		if ((entry->type == TCET_NORMAL &&
+		if (((entry->type == TCET_NORMAL &&
 		     width == entry->virtual_width &&
 		     height == entry->virtual_height &&
 		     full_format == entry->format &&
 		     entry->num_mipmaps > maxlevel) ||
 		    (entry->type == TCET_EC_DYNAMIC &&
 		     entry->native_width == width &&
-		     entry->native_height == height))
+		     entry->native_height == height)) &&
+			 entry->num_layers == 1)
 		{
 			// reuse the texture
 		}
@@ -519,6 +520,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
 		// But that will currently make the above "existing entry" tests fail as "texLevels" is not calculated until after.
 		// Currently, we might try to reuse a texture which appears to have more levels than actual, maybe..
 		entry->num_mipmaps = maxlevel + 1;
+		entry->num_layers = 1;
 		entry->type = TCET_NORMAL;
 
 		GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true);
diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h
index fbe13915fa..77bdec14fa 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.h
+++ b/Source/Core/VideoCommon/TextureCacheBase.h
@@ -39,6 +39,7 @@ public:
 		enum TexCacheEntryType type;
 
 		unsigned int num_mipmaps;
+		unsigned int num_layers;
 		unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view
 		unsigned int virtual_width, virtual_height; // Texture dimensions from OUR point of view - for hires textures or scaled EFB copies
 

From 4fe9ceeee276d2bec8cab86fdf8e5af5ed91cfc6 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 28 Oct 2014 18:50:09 +0100
Subject: [PATCH 11/66] TextureCache: Set proper vertex limit in geometry
 shader.

Without instancing 6 vertices are output instead of 3.
---
 Source/Core/VideoBackends/OGL/TextureCache.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index dce92c19d7..18e575109f 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -377,7 +377,7 @@ TextureCache::TextureCache()
 
 	const char *GProgram =
 		"layout(triangles) in;\n"
-		"layout(triangle_strip, max_vertices = 3) out;\n"
+		"layout(triangle_strip, max_vertices = 6) out;\n"
 		"in vec2 v_uv0[];\n"
 		"out vec3 f_uv0;\n"
 		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"

From b236c363de097ca8164c315e37dc369139a8a20c Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 28 Oct 2014 19:27:05 +0100
Subject: [PATCH 12/66] ShaderGen: Add a stereoscopy flag in the UID data.

---
 Source/Core/VideoCommon/PixelShaderGen.cpp  | 1 +
 Source/Core/VideoCommon/PixelShaderGen.h    | 2 ++
 Source/Core/VideoCommon/VertexShaderGen.cpp | 1 +
 Source/Core/VideoCommon/VertexShaderGen.h   | 2 ++
 4 files changed, 6 insertions(+)

diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index edad01ee77..d2124f2ad7 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -335,6 +335,7 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 		// Without MSAA, this flag is defined to have no effect.
 		out.Write("centroid in VS_OUTPUT o;\n");
 
+		uid_data->stereo = g_ActiveConfig.bStereo;
 		if (g_ActiveConfig.bStereo)
 			out.Write("flat in int eye;\n");
 
diff --git a/Source/Core/VideoCommon/PixelShaderGen.h b/Source/Core/VideoCommon/PixelShaderGen.h
index 369f940619..26ec28d33f 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.h
+++ b/Source/Core/VideoCommon/PixelShaderGen.h
@@ -119,6 +119,8 @@ struct pixel_shader_uid_data
 
 	// TODO: I think we're fine without an enablePixelLighting field, should probably double check, though..
 	LightingUidData lighting;
+
+	u32 stereo : 1;
 };
 #pragma pack()
 
diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp
index d7fd94ddec..0daaaf3275 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.cpp
+++ b/Source/Core/VideoCommon/VertexShaderGen.cpp
@@ -219,6 +219,7 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 
 	out.Write("o.pos = float4(dot(" I_PROJECTION"[0], pos), dot(" I_PROJECTION"[1], pos), dot(" I_PROJECTION"[2], pos), dot(" I_PROJECTION"[3], pos));\n");
 
+	uid_data->stereo = g_ActiveConfig.bStereo;
 	if (g_ActiveConfig.bStereo)
 		out.Write("o.rawpos = pos;\n");
 
diff --git a/Source/Core/VideoCommon/VertexShaderGen.h b/Source/Core/VideoCommon/VertexShaderGen.h
index 998f8fe6bc..a7b5e9bd3f 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.h
+++ b/Source/Core/VideoCommon/VertexShaderGen.h
@@ -56,6 +56,8 @@ struct vertex_shader_uid_data
 	} postMtxInfo[8];
 
 	LightingUidData lighting;
+
+	u32 stereo : 1;
 };
 #pragma pack()
 

From fa32f751d37604b7af46e7d9d118aacefbc5df76 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Wed, 29 Oct 2014 13:16:24 +0100
Subject: [PATCH 13/66] ShaderGen: Handle ShaderCode objects directly.

ShaderGeneratorInterface does not have virtual function members, so we have to implement each type explicitly.
---
 Source/Core/VideoBackends/D3D/LineGeometryShader.cpp  | 2 +-
 Source/Core/VideoBackends/D3D/PointGeometryShader.cpp | 2 +-
 Source/Core/VideoCommon/GeometryShaderGen.cpp         | 6 +-----
 Source/Core/VideoCommon/PixelShaderGen.cpp            | 6 +-----
 Source/Core/VideoCommon/VertexShaderGen.cpp           | 7 ++++++-
 Source/Core/VideoCommon/VertexShaderGen.h             | 3 ++-
 6 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/Source/Core/VideoBackends/D3D/LineGeometryShader.cpp b/Source/Core/VideoBackends/D3D/LineGeometryShader.cpp
index fabe6ed91a..9cd3ba6b5a 100644
--- a/Source/Core/VideoBackends/D3D/LineGeometryShader.cpp
+++ b/Source/Core/VideoBackends/D3D/LineGeometryShader.cpp
@@ -172,7 +172,7 @@ bool LineGeometryShader::SetShader(u32 components, float lineWidth,
 		static char buffer[16384];
 		ShaderCode code;
 		code.SetBuffer(buffer);
-		GenerateVSOutputStructForGS(code, API_D3D);
+		GenerateVSOutputStruct(code, API_D3D);
 		code.Write("\n%s", LINE_GS_COMMON);
 
 		std::stringstream numTexCoordsStream;
diff --git a/Source/Core/VideoBackends/D3D/PointGeometryShader.cpp b/Source/Core/VideoBackends/D3D/PointGeometryShader.cpp
index d15fe3eb0e..732ef95749 100644
--- a/Source/Core/VideoBackends/D3D/PointGeometryShader.cpp
+++ b/Source/Core/VideoBackends/D3D/PointGeometryShader.cpp
@@ -166,7 +166,7 @@ bool PointGeometryShader::SetShader(u32 components, float pointSize,
 		static char buffer[16384];
 		ShaderCode code;
 		code.SetBuffer(buffer);
-		GenerateVSOutputStructForGS(code, API_D3D);
+		GenerateVSOutputStruct(code, API_D3D);
 		code.Write("\n%s", POINT_GS_COMMON);
 
 		std::stringstream numTexCoordsStream;
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index e305551cd8..749929e8ea 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -69,11 +69,7 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 		"\tfloat4 " I_STEREOPROJECTION"[8];\n"
 		"};\n");
 
-	ShaderCode code;
-	char buf[16384];
-	code.SetBuffer(buf);
-	GenerateVSOutputStructForGS(code, ApiType);
-	out.Write(code.GetBuffer());
+	GenerateVSOutputStruct(out, ApiType);
 
 	out.Write("centroid in VS_OUTPUT v[];\n");
 	out.Write("centroid out VS_OUTPUT o;\n");
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index d2124f2ad7..771ce124e6 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -277,11 +277,7 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 		);
 	}
 
-	ShaderCode code;
-	char buf[16384];
-	code.SetBuffer(buf);
-	GenerateVSOutputStructForGS(code, ApiType);
-	out.Write(code.GetBuffer());
+	GenerateVSOutputStruct(out, ApiType);
 
 	const bool forced_early_z = g_ActiveConfig.backend_info.bSupportsEarlyZ && bpmem.UseEarlyDepthTest() && (g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED);
 	const bool per_pixel_depth = (bpmem.ztex2.op != ZTEXTURE_DISABLE && bpmem.UseLateDepthTest()) || (!g_ActiveConfig.bFastDepthCalc && bpmem.zmode.testenable && !forced_early_z);
diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp
index 0daaaf3275..374acc69ec 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.cpp
+++ b/Source/Core/VideoCommon/VertexShaderGen.cpp
@@ -467,7 +467,12 @@ void GenerateVertexShaderCode(VertexShaderCode& object, u32 components, API_TYPE
 	GenerateVertexShader<VertexShaderCode>(object, components, api_type);
 }
 
-void GenerateVSOutputStructForGS(ShaderCode& object, API_TYPE api_type)
+void GenerateVSOutputStruct(ShaderCode& object, API_TYPE api_type)
 {
 	GenerateVSOutputStruct<ShaderCode>(object, api_type);
 }
+
+void GenerateVSOutputStruct(ShaderGeneratorInterface& object, API_TYPE api_type)
+{
+	// Ignore unknown types
+}
diff --git a/Source/Core/VideoCommon/VertexShaderGen.h b/Source/Core/VideoCommon/VertexShaderGen.h
index a7b5e9bd3f..06cab3ff91 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.h
+++ b/Source/Core/VideoCommon/VertexShaderGen.h
@@ -66,4 +66,5 @@ typedef ShaderCode VertexShaderCode; // TODO: Obsolete..
 
 void GetVertexShaderUid(VertexShaderUid& object, u32 components, API_TYPE api_type);
 void GenerateVertexShaderCode(VertexShaderCode& object, u32 components, API_TYPE api_type);
-void GenerateVSOutputStructForGS(ShaderCode& object, API_TYPE api_type);
+void GenerateVSOutputStruct(ShaderCode& object, API_TYPE api_type);
+void GenerateVSOutputStruct(ShaderGeneratorInterface& object, API_TYPE api_type);

From 0a72cf94cb7a206e07e5c69d69ec88057fa33e2b Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Wed, 29 Oct 2014 13:47:24 +0100
Subject: [PATCH 14/66] TextureCache: Ignore the geometry shader if stereoscopy
 is disabled.

---
 Source/Core/VideoBackends/OGL/TextureCache.cpp | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index 18e575109f..89de707280 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -364,7 +364,7 @@ TextureCache::TextureCache()
 		"	ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];\n"
 		"}\n";
 
-	const char *VProgram =
+	const char *VProgram = (g_ActiveConfig.bStereo) ?
 		"out vec2 v_uv0;\n"
 		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"uniform vec4 copy_position;\n" // left, top, right, bottom
@@ -373,9 +373,19 @@ TextureCache::TextureCache()
 		"	vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
 		"	v_uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy);\n"
 		"	gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
+		"}\n" :
+		"out vec3 f_uv0;\n"
+		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
+		"uniform vec4 copy_position;\n" // left, top, right, bottom
+		"void main()\n"
+		"{\n"
+		"	vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
+		"	f_uv0.xy = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy);\n"
+		"	f_uv0.z = 0;\n"
+		"	gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
 		"}\n";
 
-	const char *GProgram =
+	const char *GProgram = (g_ActiveConfig.bStereo) ?
 		"layout(triangles) in;\n"
 		"layout(triangle_strip, max_vertices = 6) out;\n"
 		"in vec2 v_uv0[];\n"
@@ -393,7 +403,7 @@ TextureCache::TextureCache()
 		"		}\n"
 		"		EndPrimitive();\n"
 		"	}\n"
-		"}\n";
+		"}\n" : nullptr;
 
 	ProgramShaderCache::CompileShader(s_ColorMatrixProgram, VProgram, pColorMatrixProg, GProgram);
 	ProgramShaderCache::CompileShader(s_DepthMatrixProgram, VProgram, pDepthMatrixProg, GProgram);

From 176191dc160145827d2dc86033f3af286c70669b Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Wed, 29 Oct 2014 14:15:12 +0100
Subject: [PATCH 15/66] ShaderGenCommon: Move uniforms into a common static
 string.

---
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 14 ++------------
 Source/Core/VideoCommon/PixelShaderGen.cpp    | 14 ++------------
 Source/Core/VideoCommon/ShaderGenCommon.h     | 12 ++++++++++++
 Source/Core/VideoCommon/VertexShaderGen.cpp   | 14 ++------------
 4 files changed, 18 insertions(+), 36 deletions(-)

diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index 749929e8ea..20636cccb8 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -56,18 +56,8 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 		out.Write("layout(std140%s) uniform VSBlock {\n", g_ActiveConfig.backend_info.bSupportsBindingLayout ? ", binding = 2" : "");
 	else
 		out.Write("cbuffer VSBlock {\n");
-	out.Write(
-		"\tfloat4 " I_POSNORMALMATRIX"[6];\n"
-		"\tfloat4 " I_PROJECTION"[4];\n"
-		"\tint4 " I_MATERIALS"[4];\n"
-		"\tLight " I_LIGHTS"[8];\n"
-		"\tfloat4 " I_TEXMATRICES"[24];\n"
-		"\tfloat4 " I_TRANSFORMMATRICES"[64];\n"
-		"\tfloat4 " I_NORMALMATRICES"[32];\n"
-		"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
-		"\tfloat4 " I_DEPTHPARAMS";\n"
-		"\tfloat4 " I_STEREOPROJECTION"[8];\n"
-		"};\n");
+	out.Write(s_shader_uniforms);
+	out.Write("};\n");
 
 	GenerateVSOutputStruct(out, ApiType);
 
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index 771ce124e6..8e1adaae95 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -254,18 +254,8 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 		{
 			out.Write("cbuffer VSBlock : register(b1) {\n");
 		}
-		out.Write(
-			"\tfloat4 " I_POSNORMALMATRIX"[6];\n"
-			"\tfloat4 " I_PROJECTION"[4];\n"
-			"\tint4 " I_MATERIALS"[4];\n"
-			"\tLight " I_LIGHTS"[8];\n"
-			"\tfloat4 " I_TEXMATRICES"[24];\n"
-			"\tfloat4 " I_TRANSFORMMATRICES"[64];\n"
-			"\tfloat4 " I_NORMALMATRICES"[32];\n"
-			"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
-			"\tfloat4 " I_DEPTHPARAMS";\n"
-			"\tfloat4 " I_STEREOPROJECTION"[8];\n"
-			"};\n");
+		out.Write(s_shader_uniforms);
+		out.Write("};\n");
 	}
 
 	if (g_ActiveConfig.backend_info.bSupportsBBox)
diff --git a/Source/Core/VideoCommon/ShaderGenCommon.h b/Source/Core/VideoCommon/ShaderGenCommon.h
index b395b5e81f..11a5ccafa8 100644
--- a/Source/Core/VideoCommon/ShaderGenCommon.h
+++ b/Source/Core/VideoCommon/ShaderGenCommon.h
@@ -240,3 +240,15 @@ private:
 #define I_POSTTRANSFORMMATRICES "cpostmtx"
 #define I_DEPTHPARAMS           "cDepth" // farZ, zRange
 #define I_STEREOPROJECTION      "csproj"
+
+static const char s_shader_uniforms[] =
+	"\tfloat4 " I_POSNORMALMATRIX"[6];\n"
+	"\tfloat4 " I_PROJECTION"[4];\n"
+	"\tint4 " I_MATERIALS"[4];\n"
+	"\tLight " I_LIGHTS"[8];\n"
+	"\tfloat4 " I_TEXMATRICES"[24];\n"
+	"\tfloat4 " I_TRANSFORMMATRICES"[64];\n"
+	"\tfloat4 " I_NORMALMATRICES"[32];\n"
+	"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
+	"\tfloat4 " I_DEPTHPARAMS";\n"
+	"\tfloat4 " I_STEREOPROJECTION"[8];\n";
diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp
index 374acc69ec..aca028bda7 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.cpp
+++ b/Source/Core/VideoCommon/VertexShaderGen.cpp
@@ -92,18 +92,8 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 		out.Write("layout(std140%s) uniform VSBlock {\n", g_ActiveConfig.backend_info.bSupportsBindingLayout ? ", binding = 2" : "");
 	else
 		out.Write("cbuffer VSBlock {\n");
-	out.Write(
-		"\tfloat4 " I_POSNORMALMATRIX"[6];\n"
-		"\tfloat4 " I_PROJECTION"[4];\n"
-		"\tint4 " I_MATERIALS"[4];\n"
-		"\tLight " I_LIGHTS"[8];\n"
-		"\tfloat4 " I_TEXMATRICES"[24];\n"
-		"\tfloat4 " I_TRANSFORMMATRICES"[64];\n"
-		"\tfloat4 " I_NORMALMATRICES"[32];\n"
-		"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
-		"\tfloat4 " I_DEPTHPARAMS";\n"
-		"\tfloat4 " I_STEREOPROJECTION"[8];\n"
-		"};\n");
+	out.Write(s_shader_uniforms);
+	out.Write("};\n");
 
 	GenerateVSOutputStruct(out, api_type);
 

From d583720a59e51d75c7772d745fb541a5cf00e2b8 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Wed, 29 Oct 2014 14:51:12 +0100
Subject: [PATCH 16/66] GeometryShaderGen: Support stereoscopy on GPUs without
 support for instancing.

---
 .../Core/VideoBackends/OGL/ProgramShaderCache.cpp  |  2 +-
 Source/Core/VideoBackends/OGL/Render.cpp           |  2 +-
 Source/Core/VideoBackends/OGL/Render.h             |  1 -
 Source/Core/VideoCommon/GeometryShaderGen.cpp      | 14 +++++++++++---
 Source/Core/VideoCommon/VideoConfig.h              |  1 +
 5 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
index 4efecef4aa..de86dff177 100644
--- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
+++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
@@ -547,7 +547,7 @@ void ProgramShaderCache::CreateHeader()
 		, (g_ogl_config.bSupportSampleShading) ? "#extension GL_ARB_sample_shading : enable" : ""
 		, g_ActiveConfig.backend_info.bSupportsBindingLayout ? "#define SAMPLER_BINDING(x) layout(binding = x)" : "#define SAMPLER_BINDING(x)"
 		, g_ActiveConfig.backend_info.bSupportsBBox ? "#extension GL_ARB_shader_storage_buffer_object : enable" : ""
-		, (g_ogl_config.bSupportGSInvocation) ? "#extension GL_ARB_gpu_shader5 : enable" : ""
+		, g_ActiveConfig.backend_info.bSupportsGSInstancing ? "#extension GL_ARB_gpu_shader5 : enable" : ""
 
 		, v>=GLSLES_300 ? "precision highp float;" : ""
 		, v>=GLSLES_300 ? "precision highp int;" : ""
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index b6c288bf2c..802a1b1f96 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -476,6 +476,7 @@ Renderer::Renderer()
 				((GLExtensions::Version() >= 310) || GLExtensions::Supports("GL_NV_primitive_restart"));
 	g_Config.backend_info.bSupportsEarlyZ = GLExtensions::Supports("GL_ARB_shader_image_load_store");
 	g_Config.backend_info.bSupportsBBox = GLExtensions::Supports("GL_ARB_shader_storage_buffer_object");
+	g_Config.backend_info.bSupportsGSInstancing = GLExtensions::Supports("GL_ARB_gpu_shader5");
 
 	// Desktop OpenGL supports the binding layout if it supports 420pack
 	// OpenGL ES 3.1 supports it implicitly without an extension
@@ -491,7 +492,6 @@ Renderer::Renderer()
 	g_ogl_config.bSupportSampleShading = GLExtensions::Supports("GL_ARB_sample_shading");
 	g_ogl_config.bSupportOGL31 = GLExtensions::Version() >= 310;
 	g_ogl_config.bSupportViewportFloat = GLExtensions::Supports("GL_ARB_viewport_array");
-	g_ogl_config.bSupportGSInvocation = GLExtensions::Supports("GL_ARB_gpu_shader5");
 
 	if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3)
 	{
diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h
index 3888142c47..f35487a373 100644
--- a/Source/Core/VideoBackends/OGL/Render.h
+++ b/Source/Core/VideoBackends/OGL/Render.h
@@ -30,7 +30,6 @@ struct VideoConfig
 	GLSL_VERSION eSupportedGLSLVersion;
 	bool bSupportOGL31;
 	bool bSupportViewportFloat;
-	bool bSupportGSInvocation;
 
 	const char* gl_vendor;
 	const char* gl_renderer;
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index 20636cccb8..a6307fe722 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -45,8 +45,11 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	{
 		// Insert layout parameters
 		uid_data->stereo = g_ActiveConfig.bStereo;
-		out.Write("layout(triangles, invocations = %d) in;\n", g_ActiveConfig.bStereo ? 2 : 1);
-		out.Write("layout(triangle_strip, max_vertices = 3) out;\n");
+		if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
+			out.Write("layout(triangles, invocations = %d) in;\n", g_ActiveConfig.bStereo ? 2 : 1);
+		else
+			out.Write("layout(triangles) in;\n");
+		out.Write("layout(triangle_strip, max_vertices = %d) out;\n", g_ActiveConfig.backend_info.bSupportsGSInstancing ? 3 : 6);
 	}
 
 	out.Write("%s", s_lighting_struct);
@@ -67,9 +70,12 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("flat out int eye;\n");
 
 	out.Write("void main()\n{\n");
+	if (!g_ActiveConfig.backend_info.bSupportsGSInstancing)
+		out.Write("\tfor (eye = 0; eye < %d; ++eye) {\n", g_ActiveConfig.bStereo ? 2 : 1);
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
 	out.Write("\t\to = v[i];\n");
-	out.Write("\t\teye = gl_InvocationID;\n");
+	if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
+		out.Write("\t\teye = gl_InvocationID;\n");
 	if (g_ActiveConfig.bStereo)
 		out.Write("\t\to.pos = float4(dot(" I_STEREOPROJECTION"[eye * 4 + 0], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 1], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 2], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 3], v[i].rawpos)); \n");
 	out.Write("\t\tgl_Position = o.pos;\n");
@@ -77,6 +83,8 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("\t\tEmitVertex();\n");
 	out.Write("\t}\n");
 	out.Write("\tEndPrimitive();\n");
+	if (!g_ActiveConfig.backend_info.bSupportsGSInstancing)
+		out.Write("\t}\n");
 	out.Write("}\n");
 
 	if (is_writing_shadercode)
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 8f869dc100..5efdf17336 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -139,6 +139,7 @@ struct VideoConfig final
 		bool bSupportsEarlyZ; // needed by PixelShaderGen, so must stay in VideoCommon
 		bool bSupportsBindingLayout; // Needed by ShaderGen, so must stay in VideoCommon
 		bool bSupportsBBox;
+		bool bSupportsGSInstancing; // Needed by GeometryShaderGen, so must stay in VideoCommon
 	} backend_info;
 
 	// Utility

From 63b37e29d1c5ea6ecb2342ef9ba01b165aac602e Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Wed, 29 Oct 2014 14:57:49 +0100
Subject: [PATCH 17/66] ShaderGen: Rename "eye" to "layer".

Keeping things generic.
---
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 10 +++++-----
 Source/Core/VideoCommon/PixelShaderGen.cpp    |  4 ++--
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index a6307fe722..f016acd6f4 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -67,19 +67,19 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("centroid in VS_OUTPUT v[];\n");
 	out.Write("centroid out VS_OUTPUT o;\n");
 
-	out.Write("flat out int eye;\n");
+	out.Write("flat out int layer;\n");
 
 	out.Write("void main()\n{\n");
 	if (!g_ActiveConfig.backend_info.bSupportsGSInstancing)
-		out.Write("\tfor (eye = 0; eye < %d; ++eye) {\n", g_ActiveConfig.bStereo ? 2 : 1);
+		out.Write("\tfor (layer = 0; layer < %d; ++layer) {\n", g_ActiveConfig.bStereo ? 2 : 1);
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
 	out.Write("\t\to = v[i];\n");
 	if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
-		out.Write("\t\teye = gl_InvocationID;\n");
+		out.Write("\t\tlayer = gl_InvocationID;\n");
 	if (g_ActiveConfig.bStereo)
-		out.Write("\t\to.pos = float4(dot(" I_STEREOPROJECTION"[eye * 4 + 0], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 1], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 2], v[i].rawpos), dot(" I_STEREOPROJECTION"[eye * 4 + 3], v[i].rawpos)); \n");
+		out.Write("\t\to.pos = float4(dot(" I_STEREOPROJECTION"[layer * 4 + 0], v[i].rawpos), dot(" I_STEREOPROJECTION"[layer * 4 + 1], v[i].rawpos), dot(" I_STEREOPROJECTION"[layer * 4 + 2], v[i].rawpos), dot(" I_STEREOPROJECTION"[layer * 4 + 3], v[i].rawpos)); \n");
 	out.Write("\t\tgl_Position = o.pos;\n");
-	out.Write("\t\tgl_Layer = eye;\n");
+	out.Write("\t\tgl_Layer = layer;\n");
 	out.Write("\t\tEmitVertex();\n");
 	out.Write("\t}\n");
 	out.Write("\tEndPrimitive();\n");
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index 8e1adaae95..4f0c7ea5e1 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -323,7 +323,7 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 
 		uid_data->stereo = g_ActiveConfig.bStereo;
 		if (g_ActiveConfig.bStereo)
-			out.Write("flat in int eye;\n");
+			out.Write("flat in int layer;\n");
 
 		out.Write("void main()\n{\n");
 
@@ -926,7 +926,7 @@ static inline void SampleTexture(T& out, const char *texcoords, const char *texs
 	if (ApiType == API_D3D)
 		out.Write("iround(255.0 * Tex%d.Sample(samp%d,%s.xy * " I_TEXDIMS"[%d].xy)).%s;\n", texmap,texmap, texcoords, texmap, texswap);
 	else
-		out.Write("iround(255.0 * texture(samp%d, float3(%s.xy * " I_TEXDIMS"[%d].xy, %s))).%s;\n", texmap, texcoords, texmap, g_ActiveConfig.bStereo ? "eye" : "0.0", texswap);
+		out.Write("iround(255.0 * texture(samp%d, float3(%s.xy * " I_TEXDIMS"[%d].xy, %s))).%s;\n", texmap, texcoords, texmap, g_ActiveConfig.bStereo ? "layer" : "0.0", texswap);
 }
 
 static const char *tevAlphaFuncsTable[] =

From 284be96cd58a231785f434e7fcff956184d7192f Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Wed, 29 Oct 2014 15:42:37 +0100
Subject: [PATCH 18/66] OGL: Recreate the framebuffers when the stereo setting
 changes.

---
 Source/Core/VideoBackends/OGL/Render.cpp | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 802a1b1f96..1a0b7e5708 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -89,6 +89,8 @@ static RasterFont* s_pfont = nullptr;
 static int s_MSAASamples = 1;
 static int s_LastMultisampleMode = 0;
 
+static bool s_LastStereo = false;
+
 static u32 s_blendMode;
 
 static bool s_vsync;
@@ -591,6 +593,7 @@ Renderer::Renderer()
 	s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode;
 	s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode);
 	ApplySSAASettings();
+	s_LastStereo = g_ActiveConfig.bStereo;
 
 	// Decide framebuffer size
 	s_backbuffer_width = (int)GLInterface->GetBackBufferWidth();
@@ -1690,15 +1693,16 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 		s_LastEFBScale = g_ActiveConfig.iEFBScale;
 	}
 
-	if (xfbchanged || WindowResized || (s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode))
+	if (xfbchanged || WindowResized || (s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode) || (s_LastStereo != g_ActiveConfig.bStereo))
 	{
 		UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
 
-		if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height) || s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode)
+		if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height) || s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode || s_LastStereo != g_ActiveConfig.bStereo)
 		{
 			s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode;
 			s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode);
 			ApplySSAASettings();
+			s_LastStereo = g_ActiveConfig.bStereo;
 
 			delete g_framebuffer_manager;
 			g_framebuffer_manager = new FramebufferManager(s_target_width, s_target_height,

From 4fd943aedd9d2fbc59cd1d5181630da128e7446a Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Wed, 29 Oct 2014 16:26:28 +0100
Subject: [PATCH 19/66] VideoConfig: Limit the Stereo 3D option to the OpenGL
 backend.

---
 Source/Core/DolphinWX/VideoConfigDiag.cpp | 4 +++-
 Source/Core/VideoBackends/D3D/main.cpp    | 1 +
 Source/Core/VideoBackends/OGL/main.cpp    | 1 +
 Source/Core/VideoCommon/VideoConfig.cpp   | 1 +
 Source/Core/VideoCommon/VideoConfig.h     | 1 +
 5 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index 574b04bde3..dfa4055fa1 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -434,7 +434,9 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 
 	szr_enh->Add(CreateCheckBox(page_enh, _("Widescreen Hack"), wxGetTranslation(ws_hack_desc), vconfig.bWidescreenHack));
 	szr_enh->Add(CreateCheckBox(page_enh, _("Disable Fog"), wxGetTranslation(disable_fog_desc), vconfig.bDisableFog));
-	szr_enh->Add(CreateCheckBox(page_enh, _("Stereo 3D"), wxGetTranslation(stereo_3d_desc), vconfig.bStereo));
+	
+	if (vconfig.backend_info.bSupportsStereoscopy)
+		szr_enh->Add(CreateCheckBox(page_enh, _("Stereo 3D"), wxGetTranslation(stereo_3d_desc), vconfig.bStereo));
 
 	wxStaticBoxSizer* const group_enh = new wxStaticBoxSizer(wxVERTICAL, page_enh, _("Enhancements"));
 	group_enh->Add(szr_enh, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp
index 296936d8a7..ed5868c91b 100644
--- a/Source/Core/VideoBackends/D3D/main.cpp
+++ b/Source/Core/VideoBackends/D3D/main.cpp
@@ -78,6 +78,7 @@ void InitBackendInfo()
 	g_Config.backend_info.bSupportsPrimitiveRestart = true;
 	g_Config.backend_info.bSupportsOversizedViewports = false;
 	g_Config.backend_info.bSupportsBBox = false; // TODO: not implemented
+	g_Config.backend_info.bSupportsStereoscopy = false; // TODO: not implemented
 
 	IDXGIFactory* factory;
 	IDXGIAdapter* ad;
diff --git a/Source/Core/VideoBackends/OGL/main.cpp b/Source/Core/VideoBackends/OGL/main.cpp
index eb01d3355a..0fb36ab8d1 100644
--- a/Source/Core/VideoBackends/OGL/main.cpp
+++ b/Source/Core/VideoBackends/OGL/main.cpp
@@ -138,6 +138,7 @@ static void InitBackendInfo()
 	//g_Config.backend_info.bSupportsDualSourceBlend = true; // is gpu dependent and must be set in renderer
 	//g_Config.backend_info.bSupportsEarlyZ = true; // is gpu dependent and must be set in renderer
 	g_Config.backend_info.bSupportsOversizedViewports = true;
+	g_Config.backend_info.bSupportsStereoscopy = true;
 
 	g_Config.backend_info.Adapters.clear();
 
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index 9962160486..af073fab5e 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -203,6 +203,7 @@ void VideoConfig::VerifyValidity()
 	// TODO: Check iMaxAnisotropy value
 	if (iAdapter < 0 || iAdapter > ((int)backend_info.Adapters.size() - 1)) iAdapter = 0;
 	if (iMultisampleMode < 0 || iMultisampleMode >= (int)backend_info.AAModes.size()) iMultisampleMode = 0;
+	if (!backend_info.bSupportsStereoscopy) bStereo = false;
 }
 
 void VideoConfig::Save(const std::string& ini_file)
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 5efdf17336..b1682bab58 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -136,6 +136,7 @@ struct VideoConfig final
 		bool bSupportsDualSourceBlend;
 		bool bSupportsPrimitiveRestart;
 		bool bSupportsOversizedViewports;
+		bool bSupportsStereoscopy;
 		bool bSupportsEarlyZ; // needed by PixelShaderGen, so must stay in VideoCommon
 		bool bSupportsBindingLayout; // Needed by ShaderGen, so must stay in VideoCommon
 		bool bSupportsBBox;

From 6cacfad010b05cd9d79056a81c4dc7d19ca57273 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 30 Oct 2014 02:08:15 +0100
Subject: [PATCH 20/66] GeometryShader: Transform the projection within the
 geometry shader.

Reduces the amount of data transferred through uniforms.
The shearing transformation is reduced to a single multiplication/addition for optimization.
---
 Source/Core/VideoCommon/ConstantManager.h     |  2 +-
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 12 ++++++-----
 Source/Core/VideoCommon/ShaderGenCommon.h     |  4 ++--
 .../Core/VideoCommon/VertexShaderManager.cpp  | 20 ++++---------------
 4 files changed, 14 insertions(+), 24 deletions(-)

diff --git a/Source/Core/VideoCommon/ConstantManager.h b/Source/Core/VideoCommon/ConstantManager.h
index 1c3ad55778..e31806bbd1 100644
--- a/Source/Core/VideoCommon/ConstantManager.h
+++ b/Source/Core/VideoCommon/ConstantManager.h
@@ -43,6 +43,6 @@ struct VertexShaderConstants
 	float4 normalmatrices[32];
 	float4 posttransformmatrices[64];
 	float4 depthparams;
-	float4 stereoprojection[8];
+	float4 stereooffset;
 };
 
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index f016acd6f4..6a39e2ca1d 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -70,16 +70,18 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("flat out int layer;\n");
 
 	out.Write("void main()\n{\n");
-	if (!g_ActiveConfig.backend_info.bSupportsGSInstancing)
+	if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
+		out.Write("\tlayer = gl_InvocationID;\n");
+	else
 		out.Write("\tfor (layer = 0; layer < %d; ++layer) {\n", g_ActiveConfig.bStereo ? 2 : 1);
+	out.Write("\tgl_Layer = layer;\n");
+	out.Write("\tvec4 stereoproj = "I_PROJECTION"[0];\n");
+	out.Write("\tstereoproj[2] += "I_STEREOOFFSET"[layer] * stereoproj[0];\n");
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
 	out.Write("\t\to = v[i];\n");
-	if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
-		out.Write("\t\tlayer = gl_InvocationID;\n");
 	if (g_ActiveConfig.bStereo)
-		out.Write("\t\to.pos = float4(dot(" I_STEREOPROJECTION"[layer * 4 + 0], v[i].rawpos), dot(" I_STEREOPROJECTION"[layer * 4 + 1], v[i].rawpos), dot(" I_STEREOPROJECTION"[layer * 4 + 2], v[i].rawpos), dot(" I_STEREOPROJECTION"[layer * 4 + 3], v[i].rawpos)); \n");
+		out.Write("\t\to.pos = float4(dot(stereoproj, v[i].rawpos), dot(" I_PROJECTION"[1], v[i].rawpos), dot(" I_PROJECTION"[2], v[i].rawpos), dot(" I_PROJECTION"[3], v[i].rawpos)); \n");
 	out.Write("\t\tgl_Position = o.pos;\n");
-	out.Write("\t\tgl_Layer = layer;\n");
 	out.Write("\t\tEmitVertex();\n");
 	out.Write("\t}\n");
 	out.Write("\tEndPrimitive();\n");
diff --git a/Source/Core/VideoCommon/ShaderGenCommon.h b/Source/Core/VideoCommon/ShaderGenCommon.h
index 11a5ccafa8..5e9e24e7fa 100644
--- a/Source/Core/VideoCommon/ShaderGenCommon.h
+++ b/Source/Core/VideoCommon/ShaderGenCommon.h
@@ -239,7 +239,7 @@ private:
 #define I_NORMALMATRICES        "cnmtx"
 #define I_POSTTRANSFORMMATRICES "cpostmtx"
 #define I_DEPTHPARAMS           "cDepth" // farZ, zRange
-#define I_STEREOPROJECTION      "csproj"
+#define I_STEREOOFFSET          "csoffset"
 
 static const char s_shader_uniforms[] =
 	"\tfloat4 " I_POSNORMALMATRIX"[6];\n"
@@ -251,4 +251,4 @@ static const char s_shader_uniforms[] =
 	"\tfloat4 " I_NORMALMATRICES"[32];\n"
 	"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
 	"\tfloat4 " I_DEPTHPARAMS";\n"
-	"\tfloat4 " I_STEREOPROJECTION"[8];\n";
+	"\tfloat4 " I_STEREOOFFSET";\n";
diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp
index e40eb92152..5d65a49186 100644
--- a/Source/Core/VideoCommon/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexShaderManager.cpp
@@ -515,25 +515,13 @@ void VertexShaderManager::SetConstants()
 
 		if (g_ActiveConfig.bStereo && xfmem.projection.type == GX_PERSPECTIVE)
 		{
-			Matrix44 projMtx;
-			Matrix44::Set(projMtx, g_fProjectionMatrix);
-
-			Matrix44 leftShearMtx, rightShearMtx;
-			Matrix44::Shear(leftShearMtx, g_ActiveConfig.iStereoSeparation / (200.0f * g_ActiveConfig.iStereoFocalLength));
-			Matrix44::Shear(rightShearMtx, -g_ActiveConfig.iStereoSeparation / (200.0f * g_ActiveConfig.iStereoFocalLength));
-
-			Matrix44 leftProjMtx, rightProjMtx, leftCorrectedMtx, rightCorrectedMtx;
-			Matrix44::Multiply(projMtx, leftShearMtx, leftProjMtx);
-			Matrix44::Multiply(s_viewportCorrection, leftProjMtx, leftCorrectedMtx);
-			Matrix44::Multiply(projMtx, rightShearMtx, rightProjMtx);
-			Matrix44::Multiply(s_viewportCorrection, rightProjMtx, rightCorrectedMtx);
-			memcpy(constants.stereoprojection, leftCorrectedMtx.data, 4*16);
-			memcpy(constants.stereoprojection + 4, rightCorrectedMtx.data, 4*16);
+			float offset = g_ActiveConfig.iStereoSeparation / (200.0f * g_ActiveConfig.iStereoFocalLength);
+			constants.stereooffset[0] = offset;
+			constants.stereooffset[1] = -offset;
 		}
 		else
 		{
-			memcpy(constants.stereoprojection, constants.projection, 4 * 16);
-			memcpy(constants.stereoprojection + 4, constants.projection, 4 * 16);
+			constants.stereooffset[0] = constants.stereooffset[1] = 0;
 		}
 
 		dirty = true;

From f370cb386cccfa56e2ef677a55b27561c8217e7d Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 30 Oct 2014 14:24:23 +0100
Subject: [PATCH 21/66] ProgramShaderCache: Always generate a geometry shader
 UID, even if stereoscopy is disabled.

---
 Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp | 5 ++---
 Source/Core/VideoCommon/GeometryShaderGen.cpp        | 2 +-
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
index de86dff177..f32fa7edca 100644
--- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
+++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
@@ -220,7 +220,7 @@ SHADER* ProgramShaderCache::SetShader(DSTALPHA_MODE dstAlphaMode, u32 components
 		filename = StringFromFormat("%sps_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
 		SaveData(filename, pcode.GetBuffer());
 
-		if (g_ActiveConfig.bStereo)
+		if (gcode.GetBuffer() != nullptr)
 		{
 			filename = StringFromFormat("%sgs_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
 			SaveData(filename, gcode.GetBuffer());
@@ -387,8 +387,7 @@ void ProgramShaderCache::GetShaderId(SHADERUID* uid, DSTALPHA_MODE dstAlphaMode,
 {
 	GetPixelShaderUid(uid->puid, dstAlphaMode, API_OPENGL, components);
 	GetVertexShaderUid(uid->vuid, components, API_OPENGL);
-	if (g_ActiveConfig.bStereo)
-		GetGeometryShaderUid(uid->guid, components, API_OPENGL);
+	GetGeometryShaderUid(uid->guid, components, API_OPENGL);
 
 	if (g_ActiveConfig.bEnableShaderDebugging)
 	{
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index 6a39e2ca1d..a247899d5c 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -41,10 +41,10 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 
 	out.Write("//Geometry Shader for 3D stereoscopy\n");
 
+	uid_data->stereo = g_ActiveConfig.bStereo;
 	if (ApiType == API_OPENGL)
 	{
 		// Insert layout parameters
-		uid_data->stereo = g_ActiveConfig.bStereo;
 		if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
 			out.Write("layout(triangles, invocations = %d) in;\n", g_ActiveConfig.bStereo ? 2 : 1);
 		else

From 35342664e3f0196aed528c15dc9fadd00bce50a9 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 30 Oct 2014 14:56:40 +0100
Subject: [PATCH 22/66] OGL: Disable stereoscopy if the GPU does not support
 geometry shaders.

---
 Source/Core/VideoBackends/OGL/Render.cpp | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 1a0b7e5708..bd605c2a2b 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -507,6 +507,8 @@ Renderer::Renderer()
 			g_Config.backend_info.bSupportsBindingLayout = true;
 			g_Config.backend_info.bSupportsEarlyZ = true;
 		}
+		// TODO: OpenGL ES 3.1 provides the necessary features as extensions.
+		g_Config.backend_info.bSupportsStereoscopy = false;
 	}
 	else
 	{
@@ -521,11 +523,13 @@ Renderer::Renderer()
 		{
 			g_ogl_config.eSupportedGLSLVersion = GLSL_130;
 			g_Config.backend_info.bSupportsEarlyZ = false; // layout keyword is only supported on glsl150+
+			g_Config.backend_info.bSupportsStereoscopy = false; // geometry shaders are only supported on glsl150+
 		}
 		else if (strstr(g_ogl_config.glsl_version, "1.40"))
 		{
 			g_ogl_config.eSupportedGLSLVersion = GLSL_140;
 			g_Config.backend_info.bSupportsEarlyZ = false; // layout keyword is only supported on glsl150+
+			g_Config.backend_info.bSupportsStereoscopy = false; // geometry shaders are only supported on glsl150+
 		}
 		else
 		{
@@ -570,6 +574,7 @@ Renderer::Renderer()
 	if (g_ogl_config.max_samples < 1 || !g_ogl_config.bSupportsMSAA)
 		g_ogl_config.max_samples = 1;
 
+	g_Config.VerifyValidity();
 	UpdateActiveConfig();
 
 	OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s",
@@ -587,7 +592,8 @@ Renderer::Renderer()
 			g_ogl_config.bSupportsGLBufferStorage ? "" : "BufferStorage ",
 			g_ogl_config.bSupportsGLSync ? "" : "Sync ",
 			g_ogl_config.bSupportsMSAA ? "" : "MSAA ",
-			g_ogl_config.bSupportSampleShading ? "" : "SSAA "
+			g_ogl_config.bSupportSampleShading ? "" : "SSAA ",
+			g_ActiveConfig.backend_info.bSupportsGSInstancing ? "" : "GSInstancing "
 			);
 
 	s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode;

From 9b4185ffdf905f9e3234372fe6dc5131e90f6c93 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 30 Oct 2014 15:04:33 +0100
Subject: [PATCH 23/66] ShaderGen: Make use of padding to store the stereo
 flag.

---
 Source/Core/VideoCommon/PixelShaderGen.h  | 4 +---
 Source/Core/VideoCommon/VertexShaderGen.h | 4 +---
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/Source/Core/VideoCommon/PixelShaderGen.h b/Source/Core/VideoCommon/PixelShaderGen.h
index 26ec28d33f..784523087a 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.h
+++ b/Source/Core/VideoCommon/PixelShaderGen.h
@@ -44,7 +44,7 @@ struct pixel_shader_uid_data
 	u32 dstAlphaMode : 2;
 	u32 Pretest : 2;
 	u32 nIndirectStagesUsed : 4;
-	u32 pad0 : 1;
+	u32 stereo : 1;
 
 	u32 genMode_numtexgens : 4;
 	u32 genMode_numtevstages : 4;
@@ -119,8 +119,6 @@ struct pixel_shader_uid_data
 
 	// TODO: I think we're fine without an enablePixelLighting field, should probably double check, though..
 	LightingUidData lighting;
-
-	u32 stereo : 1;
 };
 #pragma pack()
 
diff --git a/Source/Core/VideoCommon/VertexShaderGen.h b/Source/Core/VideoCommon/VertexShaderGen.h
index 06cab3ff91..b5e9e33f29 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.h
+++ b/Source/Core/VideoCommon/VertexShaderGen.h
@@ -38,7 +38,7 @@ struct vertex_shader_uid_data
 	u32 numColorChans        : 2;
 	u32 dualTexTrans_enabled : 1;
 	u32 pixel_lighting       : 1;
-	u32 pad0                 : 1;
+	u32 stereo               : 1;
 
 	u32 texMtxInfo_n_projection : 16; // Stored separately to guarantee that the texMtxInfo struct is 8 bits wide
 	struct {
@@ -56,8 +56,6 @@ struct vertex_shader_uid_data
 	} postMtxInfo[8];
 
 	LightingUidData lighting;
-
-	u32 stereo : 1;
 };
 #pragma pack()
 

From 4d9589b35f2629c558a9a7efdee3d66497fc0fc6 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 30 Oct 2014 15:13:31 +0100
Subject: [PATCH 24/66] Cosmetics.

---
 Source/Core/VideoBackends/OGL/PostProcessing.cpp | 3 ++-
 Source/Core/VideoBackends/OGL/Render.cpp         | 2 +-
 Source/Core/VideoCommon/CMakeLists.txt           | 4 ++--
 Source/Core/VideoCommon/GeometryShaderGen.cpp    | 7 +++++++
 Source/Core/VideoCommon/TextureCacheBase.cpp     | 2 +-
 Source/Core/VideoCommon/VertexShaderGen.cpp      | 6 +-----
 6 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.cpp b/Source/Core/VideoBackends/OGL/PostProcessing.cpp
index 2681fcd345..7cd99a05d7 100644
--- a/Source/Core/VideoBackends/OGL/PostProcessing.cpp
+++ b/Source/Core/VideoBackends/OGL/PostProcessing.cpp
@@ -36,7 +36,8 @@ static char s_vertex_shader[] =
 	"	uv0 = rawpos * src_rect.zw + src_rect.xy;\n"
 	"}\n";
 
-OpenGLPostProcessing::OpenGLPostProcessing() : m_initialized(false)
+OpenGLPostProcessing::OpenGLPostProcessing()
+	: m_initialized(false)
 {
 	CreateHeader();
 
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index bd605c2a2b..9ef13ddba9 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -468,7 +468,7 @@ Renderer::Renderer()
 		glClearDepthf = ClearDepthf;
 	}
 
-	if (!(GLExtensions::Version() >= 320))
+	if (GLExtensions::Version() < 320)
 	{
 		glFramebufferTexture = FramebufferTexture;
 	}
diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt
index 14067f228f..0dfd12b732 100644
--- a/Source/Core/VideoCommon/CMakeLists.txt
+++ b/Source/Core/VideoCommon/CMakeLists.txt
@@ -9,6 +9,7 @@ set(SRCS	BoundingBox.cpp
 			Fifo.cpp
 			FPSCounter.cpp
 			FramebufferManagerBase.cpp
+			GeometryShaderGen.cpp
 			HiresTextures.cpp
 			ImageWrite.cpp
 			IndexGenerator.cpp
@@ -38,8 +39,7 @@ set(SRCS	BoundingBox.cpp
 			VideoConfig.cpp
 			VideoState.cpp
 			XFMemory.cpp
-			XFStructs.cpp
-			GeometryShaderGen.cpp)
+			XFStructs.cpp)
 set(LIBS core png)
 
 if(_M_X86)
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index a247899d5c..88be1d4098 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -70,23 +70,30 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("flat out int layer;\n");
 
 	out.Write("void main()\n{\n");
+
 	if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
 		out.Write("\tlayer = gl_InvocationID;\n");
 	else
 		out.Write("\tfor (layer = 0; layer < %d; ++layer) {\n", g_ActiveConfig.bStereo ? 2 : 1);
+
 	out.Write("\tgl_Layer = layer;\n");
 	out.Write("\tvec4 stereoproj = "I_PROJECTION"[0];\n");
 	out.Write("\tstereoproj[2] += "I_STEREOOFFSET"[layer] * stereoproj[0];\n");
+
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
 	out.Write("\t\to = v[i];\n");
+
 	if (g_ActiveConfig.bStereo)
 		out.Write("\t\to.pos = float4(dot(stereoproj, v[i].rawpos), dot(" I_PROJECTION"[1], v[i].rawpos), dot(" I_PROJECTION"[2], v[i].rawpos), dot(" I_PROJECTION"[3], v[i].rawpos)); \n");
+
 	out.Write("\t\tgl_Position = o.pos;\n");
 	out.Write("\t\tEmitVertex();\n");
 	out.Write("\t}\n");
 	out.Write("\tEndPrimitive();\n");
+
 	if (!g_ActiveConfig.backend_info.bSupportsGSInstancing)
 		out.Write("\t}\n");
+
 	out.Write("}\n");
 
 	if (is_writing_shadercode)
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index d4296cc2aa..4037a7470b 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -452,7 +452,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
 		    (entry->type == TCET_EC_DYNAMIC &&
 		     entry->native_width == width &&
 		     entry->native_height == height)) &&
-			 entry->num_layers == 1)
+		     entry->num_layers == 1)
 		{
 			// reuse the texture
 		}
diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp
index aca028bda7..7c5ca62464 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.cpp
+++ b/Source/Core/VideoCommon/VertexShaderGen.cpp
@@ -125,11 +125,7 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 				out.Write("in float%d tex%d; // ATTR%d,\n", hastexmtx ? 3 : 2, i, SHADER_TEXTURE0_ATTRIB + i);
 		}
 
-
-		if (g_ActiveConfig.bStereo)
-			out.Write("centroid out VS_OUTPUT v;\n");
-		else
-			out.Write("centroid out VS_OUTPUT o;\n");
+		out.Write("centroid out VS_OUTPUT %s;\n", (g_ActiveConfig.bStereo) ? "v" : "o");
 
 		out.Write("void main()\n{\n");
 

From 9b22e151803ccfb757eb9319f1b32e3d1999f42e Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 30 Oct 2014 23:29:56 +0100
Subject: [PATCH 25/66] VideoConfigDiag: Add stereoscopy options group.

---
 Source/Core/DolphinWX/VideoConfigDiag.cpp     | 33 +++++++++++++++++--
 Source/Core/DolphinWX/VideoConfigDiag.h       | 14 ++++++++
 .../VideoBackends/OGL/FramebufferManager.cpp  |  2 +-
 .../VideoBackends/OGL/ProgramShaderCache.cpp  |  2 +-
 Source/Core/VideoBackends/OGL/Render.cpp      | 12 +++----
 .../Core/VideoBackends/OGL/TextureCache.cpp   |  4 +--
 Source/Core/VideoCommon/GeometryShaderGen.cpp |  8 ++---
 Source/Core/VideoCommon/PixelShaderGen.cpp    |  6 ++--
 Source/Core/VideoCommon/VertexShaderGen.cpp   | 12 +++----
 .../Core/VideoCommon/VertexShaderManager.cpp  |  2 +-
 Source/Core/VideoCommon/VideoConfig.cpp       |  8 ++---
 Source/Core/VideoCommon/VideoConfig.h         |  6 ++--
 12 files changed, 75 insertions(+), 34 deletions(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index dfa4055fa1..93c7e83734 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -150,6 +150,8 @@ static wxString ppshader_desc = wxTRANSLATE("Apply a post-processing effect afte
 static wxString cache_efb_copies_desc = wxTRANSLATE("Slightly speeds up EFB to RAM copies by sacrificing emulation accuracy.\nSometimes also increases visual quality.\nIf you're experiencing any issues, try raising texture cache accuracy or disable this option.\n\nIf unsure, leave this unchecked.");
 static wxString shader_errors_desc = wxTRANSLATE("Usually if shader compilation fails, an error message is displayed.\nHowever, one may skip the popups to allow interruption free gameplay by checking this option.\n\nIf unsure, leave this unchecked.");
 static wxString stereo_3d_desc = wxTRANSLATE("Side-by-side stereoscopic 3D.");
+static wxString stereo_separation_desc = wxTRANSLATE("Control the Interpupillary distance.");
+static wxString stereo_focal_desc = wxTRANSLATE("Control the distance at which objects appear to be at screen depth.");
 
 
 #if !defined(__APPLE__)
@@ -434,14 +436,39 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 
 	szr_enh->Add(CreateCheckBox(page_enh, _("Widescreen Hack"), wxGetTranslation(ws_hack_desc), vconfig.bWidescreenHack));
 	szr_enh->Add(CreateCheckBox(page_enh, _("Disable Fog"), wxGetTranslation(disable_fog_desc), vconfig.bDisableFog));
-	
-	if (vconfig.backend_info.bSupportsStereoscopy)
-		szr_enh->Add(CreateCheckBox(page_enh, _("Stereo 3D"), wxGetTranslation(stereo_3d_desc), vconfig.bStereo));
 
 	wxStaticBoxSizer* const group_enh = new wxStaticBoxSizer(wxVERTICAL, page_enh, _("Enhancements"));
 	group_enh->Add(szr_enh, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
 	szr_enh_main->Add(group_enh, 0, wxEXPAND | wxALL, 5);
 
+	// - stereoscopy
+
+	if (vconfig.backend_info.bSupportsStereoscopy)
+	{
+		wxGridSizer* const szr_stereo = new wxGridSizer(2, 5, 5);
+
+		const wxString stereo_choices[] = { "Off", "Side-by-Side" };
+		szr_stereo->Add(new wxStaticText(page_enh, -1, _("Stereo 3D Mode:")), 1, wxALIGN_CENTER_VERTICAL, 0);
+		szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 2, stereo_choices));
+
+		wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 30, 90, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
+		sep_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoSep, this);
+		RegisterControl(sep_slider, wxGetTranslation(stereo_separation_desc));
+
+		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Interpupillary distance:")), 1, wxALIGN_CENTER_VERTICAL, 0);
+		szr_stereo->Add(sep_slider, 1, wxEXPAND | wxRIGHT);
+
+		wxSlider* const foc_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoFocalLength, 10, 200, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
+		foc_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoFoc, this);
+		RegisterControl(foc_slider, wxGetTranslation(stereo_focal_desc));
+
+		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Focal Length:")), 1, wxALIGN_CENTER_VERTICAL, 0);
+		szr_stereo->Add(foc_slider, 1, wxEXPAND | wxRIGHT);
+
+		wxStaticBoxSizer* const group_stereo = new wxStaticBoxSizer(wxVERTICAL, page_enh, _("Stereoscopy"));
+		group_stereo->Add(szr_stereo, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
+		szr_enh_main->Add(group_stereo, 0, wxEXPAND | wxALL, 5);
+	}
 
 	szr_enh_main->AddStretchSpacer();
 	CreateDescriptionArea(page_enh, szr_enh_main);
diff --git a/Source/Core/DolphinWX/VideoConfigDiag.h b/Source/Core/DolphinWX/VideoConfigDiag.h
index bd242d3a99..2d21912c1c 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.h
+++ b/Source/Core/DolphinWX/VideoConfigDiag.h
@@ -165,6 +165,20 @@ protected:
 		ev.Skip();
 	}
 
+	void Event_StereoSep(wxCommandEvent &ev)
+	{
+		vconfig.iStereoSeparation = ev.GetInt();
+
+		ev.Skip();
+	}
+
+	void Event_StereoFoc(wxCommandEvent &ev)
+	{
+		vconfig.iStereoFocalLength = ev.GetInt();
+
+		ev.Skip();
+	}
+
 	void Event_ClickClose(wxCommandEvent&);
 	void Event_Close(wxCloseEvent&);
 
diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
index 8603c3b030..dcc5791dfc 100644
--- a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
+++ b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
@@ -73,7 +73,7 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
 	m_efbDepth = glObj[1];
 	m_efbColorSwap = glObj[2];
 
-	m_EFBLayers = (g_ActiveConfig.bStereo) ? 2 : 1;
+	m_EFBLayers = (g_ActiveConfig.iStereoMode > 0) ? 2 : 1;
 
 	// OpenGL MSAA textures are a different kind of texture type and must be allocated
 	// with a different function, so we create them separately.
diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
index f32fa7edca..69dbbac063 100644
--- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
+++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
@@ -200,7 +200,7 @@ SHADER* ProgramShaderCache::SetShader(DSTALPHA_MODE dstAlphaMode, u32 components
 	ShaderCode gcode;
 	GenerateVertexShaderCode(vcode, components, API_OPENGL);
 	GeneratePixelShaderCode(pcode, dstAlphaMode, API_OPENGL, components);
-	if (g_ActiveConfig.bStereo)
+	if (g_ActiveConfig.iStereoMode > 0)
 		GenerateGeometryShaderCode(gcode, components, API_OPENGL);
 
 	if (g_ActiveConfig.bEnableShaderDebugging)
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 9ef13ddba9..58d3fd0ee3 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -599,7 +599,7 @@ Renderer::Renderer()
 	s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode;
 	s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode);
 	ApplySSAASettings();
-	s_LastStereo = g_ActiveConfig.bStereo;
+	s_LastStereo = g_ActiveConfig.iStereoMode > 0;
 
 	// Decide framebuffer size
 	s_backbuffer_width = (int)GLInterface->GetBackBufferWidth();
@@ -1514,7 +1514,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 
 			sourceRc.right -= fbStride - fbWidth;
 
-			if (g_ActiveConfig.bStereo)
+			if (g_ActiveConfig.iStereoMode == 1)
 			{
 				TargetRectangle leftRc = drawRc, rightRc = drawRc;
 				int width = drawRc.right - drawRc.left;
@@ -1537,7 +1537,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 		// for msaa mode, we must resolve the efb content to non-msaa
 		GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(rc);
 
-		if (g_ActiveConfig.bStereo)
+		if (g_ActiveConfig.iStereoMode == 1)
 		{
 			TargetRectangle leftRc = flipped_trc, rightRc = flipped_trc;
 			int width = flipped_trc.right - flipped_trc.left;
@@ -1699,16 +1699,16 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 		s_LastEFBScale = g_ActiveConfig.iEFBScale;
 	}
 
-	if (xfbchanged || WindowResized || (s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode) || (s_LastStereo != g_ActiveConfig.bStereo))
+	if (xfbchanged || WindowResized || (s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode) || (s_LastStereo != (g_ActiveConfig.iStereoMode > 0)))
 	{
 		UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height);
 
-		if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height) || s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode || s_LastStereo != g_ActiveConfig.bStereo)
+		if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height) || s_LastMultisampleMode != g_ActiveConfig.iMultisampleMode || s_LastStereo != (g_ActiveConfig.iStereoMode > 0))
 		{
 			s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode;
 			s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode);
 			ApplySSAASettings();
-			s_LastStereo = g_ActiveConfig.bStereo;
+			s_LastStereo = g_ActiveConfig.iStereoMode > 0;
 
 			delete g_framebuffer_manager;
 			g_framebuffer_manager = new FramebufferManager(s_target_width, s_target_height,
diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index 89de707280..f58a71fc92 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -364,7 +364,7 @@ TextureCache::TextureCache()
 		"	ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];\n"
 		"}\n";
 
-	const char *VProgram = (g_ActiveConfig.bStereo) ?
+	const char *VProgram = (g_ActiveConfig.iStereoMode > 0) ?
 		"out vec2 v_uv0;\n"
 		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"uniform vec4 copy_position;\n" // left, top, right, bottom
@@ -385,7 +385,7 @@ TextureCache::TextureCache()
 		"	gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
 		"}\n";
 
-	const char *GProgram = (g_ActiveConfig.bStereo) ?
+	const char *GProgram = (g_ActiveConfig.iStereoMode > 0) ?
 		"layout(triangles) in;\n"
 		"layout(triangle_strip, max_vertices = 6) out;\n"
 		"in vec2 v_uv0[];\n"
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index 88be1d4098..cded3bdea1 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -41,12 +41,12 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 
 	out.Write("//Geometry Shader for 3D stereoscopy\n");
 
-	uid_data->stereo = g_ActiveConfig.bStereo;
+	uid_data->stereo = g_ActiveConfig.iStereoMode > 0;
 	if (ApiType == API_OPENGL)
 	{
 		// Insert layout parameters
 		if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
-			out.Write("layout(triangles, invocations = %d) in;\n", g_ActiveConfig.bStereo ? 2 : 1);
+			out.Write("layout(triangles, invocations = %d) in;\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1);
 		else
 			out.Write("layout(triangles) in;\n");
 		out.Write("layout(triangle_strip, max_vertices = %d) out;\n", g_ActiveConfig.backend_info.bSupportsGSInstancing ? 3 : 6);
@@ -74,7 +74,7 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
 		out.Write("\tlayer = gl_InvocationID;\n");
 	else
-		out.Write("\tfor (layer = 0; layer < %d; ++layer) {\n", g_ActiveConfig.bStereo ? 2 : 1);
+		out.Write("\tfor (layer = 0; layer < %d; ++layer) {\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1);
 
 	out.Write("\tgl_Layer = layer;\n");
 	out.Write("\tvec4 stereoproj = "I_PROJECTION"[0];\n");
@@ -83,7 +83,7 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
 	out.Write("\t\to = v[i];\n");
 
-	if (g_ActiveConfig.bStereo)
+	if (g_ActiveConfig.iStereoMode > 0)
 		out.Write("\t\to.pos = float4(dot(stereoproj, v[i].rawpos), dot(" I_PROJECTION"[1], v[i].rawpos), dot(" I_PROJECTION"[2], v[i].rawpos), dot(" I_PROJECTION"[3], v[i].rawpos)); \n");
 
 	out.Write("\t\tgl_Position = o.pos;\n");
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index 4f0c7ea5e1..11c9679ca9 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -321,8 +321,8 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 		// Without MSAA, this flag is defined to have no effect.
 		out.Write("centroid in VS_OUTPUT o;\n");
 
-		uid_data->stereo = g_ActiveConfig.bStereo;
-		if (g_ActiveConfig.bStereo)
+		uid_data->stereo = g_ActiveConfig.iStereoMode > 0;
+		if (g_ActiveConfig.iStereoMode > 0)
 			out.Write("flat in int layer;\n");
 
 		out.Write("void main()\n{\n");
@@ -926,7 +926,7 @@ static inline void SampleTexture(T& out, const char *texcoords, const char *texs
 	if (ApiType == API_D3D)
 		out.Write("iround(255.0 * Tex%d.Sample(samp%d,%s.xy * " I_TEXDIMS"[%d].xy)).%s;\n", texmap,texmap, texcoords, texmap, texswap);
 	else
-		out.Write("iround(255.0 * texture(samp%d, float3(%s.xy * " I_TEXDIMS"[%d].xy, %s))).%s;\n", texmap, texcoords, texmap, g_ActiveConfig.bStereo ? "layer" : "0.0", texswap);
+		out.Write("iround(255.0 * texture(samp%d, float3(%s.xy * " I_TEXDIMS"[%d].xy, %s))).%s;\n", texmap, texcoords, texmap, g_ActiveConfig.iStereoMode > 0 ? "layer" : "0.0", texswap);
 }
 
 static const char *tevAlphaFuncsTable[] =
diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp
index 7c5ca62464..fd2ee7f3e6 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.cpp
+++ b/Source/Core/VideoCommon/VertexShaderGen.cpp
@@ -52,7 +52,7 @@ static inline void GenerateVSOutputStruct(T& object, API_TYPE api_type)
 	if (g_ActiveConfig.bEnablePixelLighting)
 		DefineVSOutputStructMember(object, api_type, "float4", "Normal", -1, "TEXCOORD", xfmem.numTexGen.numTexGens + 1);
 
-	if (g_ActiveConfig.bStereo)
+	if (g_ActiveConfig.iStereoMode > 0)
 		DefineVSOutputStructMember(object, api_type, "float4", "rawpos", -1, "POSITION");
 
 	object.Write("};\n");
@@ -125,11 +125,11 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 				out.Write("in float%d tex%d; // ATTR%d,\n", hastexmtx ? 3 : 2, i, SHADER_TEXTURE0_ATTRIB + i);
 		}
 
-		out.Write("centroid out VS_OUTPUT %s;\n", (g_ActiveConfig.bStereo) ? "v" : "o");
+		out.Write("centroid out VS_OUTPUT %s;\n", (g_ActiveConfig.iStereoMode > 0) ? "v" : "o");
 
 		out.Write("void main()\n{\n");
 
-		if (g_ActiveConfig.bStereo)
+		if (g_ActiveConfig.iStereoMode > 0)
 			out.Write("VS_OUTPUT o;\n");
 	}
 	else // D3D
@@ -205,8 +205,8 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 
 	out.Write("o.pos = float4(dot(" I_PROJECTION"[0], pos), dot(" I_PROJECTION"[1], pos), dot(" I_PROJECTION"[2], pos), dot(" I_PROJECTION"[3], pos));\n");
 
-	uid_data->stereo = g_ActiveConfig.bStereo;
-	if (g_ActiveConfig.bStereo)
+	uid_data->stereo = g_ActiveConfig.iStereoMode > 0;
+	if (g_ActiveConfig.iStereoMode > 0)
 		out.Write("o.rawpos = pos;\n");
 
 	out.Write("int4 lacc;\n"
@@ -420,7 +420,7 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 
 	if (api_type == API_OPENGL)
 	{
-		if (g_ActiveConfig.bStereo)
+		if (g_ActiveConfig.iStereoMode > 0)
 			out.Write("v = o;\n");
 
 		out.Write("gl_Position = o.pos;\n");
diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp
index 5d65a49186..ad69ba4362 100644
--- a/Source/Core/VideoCommon/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexShaderManager.cpp
@@ -513,7 +513,7 @@ void VertexShaderManager::SetConstants()
 			memcpy(constants.projection, correctedMtx.data, 4*16);
 		}
 
-		if (g_ActiveConfig.bStereo && xfmem.projection.type == GX_PERSPECTIVE)
+		if (g_ActiveConfig.iStereoMode > 0 && xfmem.projection.type == GX_PERSPECTIVE)
 		{
 			float offset = g_ActiveConfig.iStereoSeparation / (200.0f * g_ActiveConfig.iStereoFocalLength);
 			constants.stereooffset[0] = offset;
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index af073fab5e..18e098652d 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -66,7 +66,7 @@ void VideoConfig::Load(const std::string& ini_file)
 	settings->Get("DumpEFBTarget", &bDumpEFBTarget, 0);
 	settings->Get("FreeLook", &bFreeLook, 0);
 	settings->Get("UseFFV1", &bUseFFV1, 0);
-	settings->Get("Stereo", &bStereo, false);
+	settings->Get("StereoMode", &iStereoMode, 0);
 	settings->Get("StereoSeparation", &iStereoSeparation, 65);
 	settings->Get("StereoFocalLength", &iStereoFocalLength, 100);
 	settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
@@ -140,7 +140,7 @@ void VideoConfig::GameIniLoad()
 	CHECK_SETTING("Video_Settings", "UseRealXFB", bUseRealXFB);
 	CHECK_SETTING("Video_Settings", "SafeTextureCacheColorSamples", iSafeTextureCache_ColorSamples);
 	CHECK_SETTING("Video_Settings", "HiresTextures", bHiresTextures);
-	CHECK_SETTING("Video_Settings", "Stereo", bStereo);
+	CHECK_SETTING("Video_Settings", "StereoMode", iStereoMode);
 	CHECK_SETTING("Video_Settings", "StereoSeparation", iStereoSeparation);
 	CHECK_SETTING("Video_Settings", "StereoFocalLength", iStereoFocalLength);
 	CHECK_SETTING("Video_Settings", "EnablePixelLighting", bEnablePixelLighting);
@@ -203,7 +203,7 @@ void VideoConfig::VerifyValidity()
 	// TODO: Check iMaxAnisotropy value
 	if (iAdapter < 0 || iAdapter > ((int)backend_info.Adapters.size() - 1)) iAdapter = 0;
 	if (iMultisampleMode < 0 || iMultisampleMode >= (int)backend_info.AAModes.size()) iMultisampleMode = 0;
-	if (!backend_info.bSupportsStereoscopy) bStereo = false;
+	if (!backend_info.bSupportsStereoscopy) iStereoMode = 0;
 }
 
 void VideoConfig::Save(const std::string& ini_file)
@@ -231,7 +231,7 @@ void VideoConfig::Save(const std::string& ini_file)
 	settings->Set("DumpEFBTarget", bDumpEFBTarget);
 	settings->Set("FreeLook", bFreeLook);
 	settings->Set("UseFFV1", bUseFFV1);
-	settings->Set("Stereo", bStereo);
+	settings->Set("StereoMode", iStereoMode);
 	settings->Set("StereoSeparation", iStereoSeparation);
 	settings->Set("StereoFocalLength", iStereoFocalLength);
 	settings->Set("EnablePixelLighting", bEnablePixelLighting);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index b1682bab58..a44b0a78a2 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -71,6 +71,9 @@ struct VideoConfig final
 	bool bForceFiltering;
 	int iMaxAnisotropy;
 	std::string sPostProcessingShader;
+	int iStereoMode;
+	int iStereoSeparation;
+	int iStereoFocalLength;
 
 	// Information
 	bool bShowFPS;
@@ -92,9 +95,6 @@ struct VideoConfig final
 	bool bDumpEFBTarget;
 	bool bUseFFV1;
 	bool bFreeLook;
-	bool bStereo;
-	int iStereoSeparation;
-	int iStereoFocalLength;
 	bool bBorderlessFullscreen;
 
 	// Hacks

From d7804a4d3e9a5cbc5347148a0e37aa669e9e9e2b Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 30 Oct 2014 23:40:03 +0100
Subject: [PATCH 26/66] Cosmetics.

---
 Source/Core/VideoBackends/OGL/ProgramShaderCache.h | 2 +-
 Source/Core/VideoCommon/GeometryShaderGen.cpp      | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h
index 7286ade6ef..5c95313fa7 100644
--- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h
+++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h
@@ -7,9 +7,9 @@
 #include "Common/LinearDiskCache.h"
 #include "Core/ConfigManager.h"
 #include "VideoBackends/OGL/GLUtil.h"
+#include "VideoCommon/GeometryShaderGen.h"
 #include "VideoCommon/PixelShaderGen.h"
 #include "VideoCommon/VertexShaderGen.h"
-#include "VideoCommon/GeometryShaderGen.h"
 
 namespace OGL
 {
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index cded3bdea1..57fc4b7d8b 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -9,9 +9,9 @@
 #endif
 
 #include "VideoCommon/GeometryShaderGen.h"
-#include "VideoCommon/VideoConfig.h"
-#include "VideoCommon/VertexShaderGen.h"
 #include "VideoCommon/LightingShaderGen.h"
+#include "VideoCommon/VertexShaderGen.h"
+#include "VideoCommon/VideoConfig.h"
 
 static char text[16384];
 
@@ -77,8 +77,8 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 		out.Write("\tfor (layer = 0; layer < %d; ++layer) {\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1);
 
 	out.Write("\tgl_Layer = layer;\n");
-	out.Write("\tvec4 stereoproj = "I_PROJECTION"[0];\n");
-	out.Write("\tstereoproj[2] += "I_STEREOOFFSET"[layer] * stereoproj[0];\n");
+	out.Write("\tvec4 stereoproj = " I_PROJECTION"[0];\n");
+	out.Write("\tstereoproj[2] += " I_STEREOOFFSET"[layer] * stereoproj[0];\n");
 
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
 	out.Write("\t\to = v[i];\n");

From 0eb0c47eba57366bf80d7c69b3a07b5924102c1c Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 15:24:04 +0100
Subject: [PATCH 27/66] Render: Improve SBS presentation.

New calculation properly takes pillar boxing into account.
---
 Source/Core/VideoBackends/OGL/Render.cpp | 26 ++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 58d3fd0ee3..bba030f209 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -1516,10 +1516,17 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 
 			if (g_ActiveConfig.iStereoMode == 1)
 			{
+				// Resize target to half its original size
+				int width = drawRc.GetWidth();
+				drawRc.left += width / 4;
+				drawRc.right -= width / 4;
+
+				// Create two target rectangle offset to the sides of the backbuffer
 				TargetRectangle leftRc = drawRc, rightRc = drawRc;
-				int width = drawRc.right - drawRc.left;
-				leftRc.right -= width / 2;
-				rightRc.left += width / 2;
+				leftRc.left -= s_backbuffer_width / 4;
+				leftRc.right -= s_backbuffer_width / 4;
+				rightRc.left += s_backbuffer_width / 4;
+				rightRc.right += s_backbuffer_width / 4;
 
 				m_post_processor->BlitFromTexture(sourceRc, leftRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 0);
 				m_post_processor->BlitFromTexture(sourceRc, rightRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 1);
@@ -1539,10 +1546,17 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 
 		if (g_ActiveConfig.iStereoMode == 1)
 		{
+			// Resize target to half its original size
+			int width = flipped_trc.GetWidth();
+			flipped_trc.left += width / 4;
+			flipped_trc.right -= width / 4;
+
+			// Create two target rectangle offset to the sides of the backbuffer
 			TargetRectangle leftRc = flipped_trc, rightRc = flipped_trc;
-			int width = flipped_trc.right - flipped_trc.left;
-			leftRc.right -= width / 2;
-			rightRc.left += width / 2;
+			leftRc.left -= s_backbuffer_width / 4;
+			leftRc.right -= s_backbuffer_width / 4;
+			rightRc.left += s_backbuffer_width / 4;
+			rightRc.right += s_backbuffer_width / 4;
 
 			m_post_processor->BlitFromTexture(targetRc, leftRc, tex, s_target_width, s_target_height, 0);
 			m_post_processor->BlitFromTexture(targetRc, rightRc, tex, s_target_width, s_target_height, 1);

From 6c8f3fa8616381ea2957a2a00834a490407b9b2a Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 15:25:42 +0100
Subject: [PATCH 28/66] VideoConfig: Add StereoMode enumeration.

---
 Source/Core/VideoBackends/OGL/Render.cpp | 4 ++--
 Source/Core/VideoCommon/VideoConfig.h    | 6 ++++++
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index bba030f209..484232f3cc 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -1514,7 +1514,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 
 			sourceRc.right -= fbStride - fbWidth;
 
-			if (g_ActiveConfig.iStereoMode == 1)
+			if (g_ActiveConfig.iStereoMode == STEREO_SBS)
 			{
 				// Resize target to half its original size
 				int width = drawRc.GetWidth();
@@ -1544,7 +1544,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 		// for msaa mode, we must resolve the efb content to non-msaa
 		GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(rc);
 
-		if (g_ActiveConfig.iStereoMode == 1)
+		if (g_ActiveConfig.iStereoMode == STEREO_SBS)
 		{
 			// Resize target to half its original size
 			int width = flipped_trc.GetWidth();
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index a44b0a78a2..2cd83e64c5 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -44,6 +44,12 @@ enum EFBScale
 	SCALE_4X,
 };
 
+enum StereoMode
+{
+	STEREO_OFF = 0,
+	STEREO_SBS
+};
+
 // NEVER inherit from this class.
 struct VideoConfig final
 {

From c3ad6e78207bc0f64a9687fd8f27fbe0f7606bc7 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 15:53:08 +0100
Subject: [PATCH 29/66] PostProcessing: Add support for anaglyph stereoscopy
 mode.

---
 Source/Core/DolphinWX/VideoConfigDiag.cpp        | 11 ++++++++---
 Source/Core/DolphinWX/VideoConfigDiag.h          |  8 ++++++++
 Source/Core/VideoBackends/OGL/PostProcessing.cpp | 16 ++++++++++++----
 Source/Core/VideoCommon/VideoConfig.h            |  3 ++-
 4 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index 93c7e83734..9b8b70b5d5 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -399,7 +399,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 	if (vconfig.backend_info.PPShaders.size())
 	{
 		wxFlexGridSizer* const szr_pp = new wxFlexGridSizer(3, 5, 5);
-		wxChoice *const choice_ppshader = new wxChoice(page_enh, -1);
+		choice_ppshader = new wxChoice(page_enh, -1);
 		RegisterControl(choice_ppshader, wxGetTranslation(ppshader_desc));
 		choice_ppshader->AppendString(_("(off)"));
 
@@ -428,6 +428,11 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 		szr_pp->Add(button_config_pp);
 		szr_enh->Add(szr_pp);
 	}
+	else
+	{
+		choice_ppshader = nullptr;
+		button_config_pp = nullptr;
+	}
 
 	// Scaled copy, PL, Bilinear filter
 	szr_enh->Add(CreateCheckBox(page_enh, _("Scaled EFB Copy"), wxGetTranslation(scaled_efb_copy_desc), vconfig.bCopyEFBScaled));
@@ -447,9 +452,9 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 	{
 		wxGridSizer* const szr_stereo = new wxGridSizer(2, 5, 5);
 
-		const wxString stereo_choices[] = { "Off", "Side-by-Side" };
+		const wxString stereo_choices[] = { "Off", "Side-by-Side", "Anaglyph" };
 		szr_stereo->Add(new wxStaticText(page_enh, -1, _("Stereo 3D Mode:")), 1, wxALIGN_CENTER_VERTICAL, 0);
-		szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 2, stereo_choices));
+		szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 3, stereo_choices));
 
 		wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 30, 90, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
 		sep_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoSep, this);
diff --git a/Source/Core/DolphinWX/VideoConfigDiag.h b/Source/Core/DolphinWX/VideoConfigDiag.h
index 2d21912c1c..7900039bea 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.h
+++ b/Source/Core/DolphinWX/VideoConfigDiag.h
@@ -198,6 +198,12 @@ protected:
 		virtual_xfb->Enable(vconfig.bUseXFB);
 		real_xfb->Enable(vconfig.bUseXFB);
 
+		// PP Shaders
+		if (choice_ppshader)
+			choice_ppshader->Enable(vconfig.iStereoMode != STEREO_ANAGLYPH);
+		if (button_config_pp)
+			button_config_pp->Enable(vconfig.iStereoMode != STEREO_ANAGLYPH);
+
 		// Things which shouldn't be changed during emulation
 		if (Core::IsRunning())
 		{
@@ -262,6 +268,8 @@ protected:
 
 	wxCheckBox* progressive_scan_checkbox;
 
+	wxChoice* choice_ppshader;
+
 	std::map<wxWindow*, wxString> ctrl_descs; // maps setting controls to their descriptions
 	std::map<wxWindow*, wxStaticText*> desc_texts; // maps dialog tabs (which are the parents of the setting controls) to their description text objects
 
diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.cpp b/Source/Core/VideoBackends/OGL/PostProcessing.cpp
index 7cd99a05d7..905531226c 100644
--- a/Source/Core/VideoBackends/OGL/PostProcessing.cpp
+++ b/Source/Core/VideoBackends/OGL/PostProcessing.cpp
@@ -166,10 +166,13 @@ void OpenGLPostProcessing::ApplyShader()
 	m_shader.Destroy();
 	m_uniform_bindings.clear();
 
-	// load shader from disk
-	std::string default_shader = "void main() { SetOutput(Sample()); }\n";
+	// load shader code
 	std::string code = "";
-	if (g_ActiveConfig.sPostProcessingShader != "")
+	std::string default_shader = "void main() { SetOutput(Sample()); }\n";
+
+	if (g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH)
+		code = "void main() { SetOutput(float4(SampleLayer(1).r, SampleLayer(0).gba)); }\n";
+	else if (g_ActiveConfig.sPostProcessingShader != "")
 		code = m_config.LoadShader();
 
 	if (code == "")
@@ -253,7 +256,12 @@ void OpenGLPostProcessing::CreateHeader()
 			"\treturn texture(samp9, float3(location, layer));\n"
 		"}\n"
 
-		"#define SampleOffset(offset) textureOffset(samp9, uv0, offset)\n"
+		"float4 SampleLayer(int layer)\n"
+		"{\n"
+			"\treturn texture(samp9, float3(uv0, layer));\n"
+		"}\n"
+
+		"#define SampleOffset(offset) textureOffset(samp9, float3(uv0, layer), offset)\n"
 
 		"float4 SampleFontLocation(float2 location)\n"
 		"{\n"
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 2cd83e64c5..2eec352021 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -47,7 +47,8 @@ enum EFBScale
 enum StereoMode
 {
 	STEREO_OFF = 0,
-	STEREO_SBS
+	STEREO_SBS,
+	STEREO_ANAGLYPH
 };
 
 // NEVER inherit from this class.

From 1261bd02caa0ca2503bdd52e7b37c22f8e0bcde5 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 16:05:44 +0100
Subject: [PATCH 30/66] VertexShaderManager: Add stereoscopy options to swap
 the left and right eye.

---
 Source/Core/DolphinWX/VideoConfigDiag.cpp       | 3 +++
 Source/Core/VideoCommon/VertexShaderManager.cpp | 4 ++--
 Source/Core/VideoCommon/VideoConfig.cpp         | 3 +++
 Source/Core/VideoCommon/VideoConfig.h           | 1 +
 4 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index 9b8b70b5d5..78be8c208e 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -152,6 +152,7 @@ static wxString shader_errors_desc = wxTRANSLATE("Usually if shader compilation
 static wxString stereo_3d_desc = wxTRANSLATE("Side-by-side stereoscopic 3D.");
 static wxString stereo_separation_desc = wxTRANSLATE("Control the Interpupillary distance.");
 static wxString stereo_focal_desc = wxTRANSLATE("Control the distance at which objects appear to be at screen depth.");
+static wxString stereo_swap_desc = wxTRANSLATE("Swap the left and right eye.\n\nIf unsure, leave this unchecked.");
 
 
 #if !defined(__APPLE__)
@@ -470,6 +471,8 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Focal Length:")), 1, wxALIGN_CENTER_VERTICAL, 0);
 		szr_stereo->Add(foc_slider, 1, wxEXPAND | wxRIGHT);
 
+		szr_stereo->Add(CreateCheckBox(page_enh, _("Swap eyes"), wxGetTranslation(stereo_swap_desc), vconfig.bStereoSwapEyes));
+
 		wxStaticBoxSizer* const group_stereo = new wxStaticBoxSizer(wxVERTICAL, page_enh, _("Stereoscopy"));
 		group_stereo->Add(szr_stereo, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
 		szr_enh_main->Add(group_stereo, 0, wxEXPAND | wxALL, 5);
diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp
index ad69ba4362..150bd783a8 100644
--- a/Source/Core/VideoCommon/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexShaderManager.cpp
@@ -516,8 +516,8 @@ void VertexShaderManager::SetConstants()
 		if (g_ActiveConfig.iStereoMode > 0 && xfmem.projection.type == GX_PERSPECTIVE)
 		{
 			float offset = g_ActiveConfig.iStereoSeparation / (200.0f * g_ActiveConfig.iStereoFocalLength);
-			constants.stereooffset[0] = offset;
-			constants.stereooffset[1] = -offset;
+			constants.stereooffset[0] = (g_ActiveConfig.bStereoSwapEyes) ? -offset : offset;
+			constants.stereooffset[1] = (g_ActiveConfig.bStereoSwapEyes) ? offset : -offset;
 		}
 		else
 		{
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index 18e098652d..4bcb2051f0 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -69,6 +69,7 @@ void VideoConfig::Load(const std::string& ini_file)
 	settings->Get("StereoMode", &iStereoMode, 0);
 	settings->Get("StereoSeparation", &iStereoSeparation, 65);
 	settings->Get("StereoFocalLength", &iStereoFocalLength, 100);
+	settings->Get("StereoSwapEyes", &bStereoSwapEyes, false);
 	settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
 	settings->Get("FastDepthCalc", &bFastDepthCalc, true);
 	settings->Get("MSAA", &iMultisampleMode, 0);
@@ -143,6 +144,7 @@ void VideoConfig::GameIniLoad()
 	CHECK_SETTING("Video_Settings", "StereoMode", iStereoMode);
 	CHECK_SETTING("Video_Settings", "StereoSeparation", iStereoSeparation);
 	CHECK_SETTING("Video_Settings", "StereoFocalLength", iStereoFocalLength);
+	CHECK_SETTING("Video_Settings", "StereoSwapEyes", bStereoSwapEyes);
 	CHECK_SETTING("Video_Settings", "EnablePixelLighting", bEnablePixelLighting);
 	CHECK_SETTING("Video_Settings", "FastDepthCalc", bFastDepthCalc);
 	CHECK_SETTING("Video_Settings", "MSAA", iMultisampleMode);
@@ -234,6 +236,7 @@ void VideoConfig::Save(const std::string& ini_file)
 	settings->Set("StereoMode", iStereoMode);
 	settings->Set("StereoSeparation", iStereoSeparation);
 	settings->Set("StereoFocalLength", iStereoFocalLength);
+	settings->Set("StereoSwapEyes", bStereoSwapEyes);
 	settings->Set("EnablePixelLighting", bEnablePixelLighting);
 	settings->Set("FastDepthCalc", bFastDepthCalc);
 	settings->Set("ShowEFBCopyRegions", bShowEFBCopyRegions);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 2eec352021..a03e70bdc6 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -81,6 +81,7 @@ struct VideoConfig final
 	int iStereoMode;
 	int iStereoSeparation;
 	int iStereoFocalLength;
+	bool bStereoSwapEyes;
 
 	// Information
 	bool bShowFPS;

From f74d1b16ed397533b75d7f1fa910d0d901004819 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 16:24:35 +0100
Subject: [PATCH 31/66] OGL: Add Top-and-Bottom stereoscopy mode.

---
 Source/Core/DolphinWX/VideoConfigDiag.cpp |  4 +--
 Source/Core/VideoBackends/OGL/Render.cpp  | 30 ++++---------------
 Source/Core/VideoCommon/RenderBase.cpp    | 36 +++++++++++++++++++++++
 Source/Core/VideoCommon/RenderBase.h      |  2 ++
 Source/Core/VideoCommon/VideoConfig.h     |  1 +
 5 files changed, 47 insertions(+), 26 deletions(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index 78be8c208e..5e4dda014d 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -453,9 +453,9 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 	{
 		wxGridSizer* const szr_stereo = new wxGridSizer(2, 5, 5);
 
-		const wxString stereo_choices[] = { "Off", "Side-by-Side", "Anaglyph" };
+		const wxString stereo_choices[] = { "Off", "Side-by-Side", "Top-and-Bottom", "Anaglyph" };
 		szr_stereo->Add(new wxStaticText(page_enh, -1, _("Stereo 3D Mode:")), 1, wxALIGN_CENTER_VERTICAL, 0);
-		szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 3, stereo_choices));
+		szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 4, stereo_choices));
 
 		wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 30, 90, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
 		sep_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoSep, this);
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 484232f3cc..ff6f9f25df 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -1514,19 +1514,10 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 
 			sourceRc.right -= fbStride - fbWidth;
 
-			if (g_ActiveConfig.iStereoMode == STEREO_SBS)
+			if (g_ActiveConfig.iStereoMode == STEREO_SBS || g_ActiveConfig.iStereoMode == STEREO_TAB)
 			{
-				// Resize target to half its original size
-				int width = drawRc.GetWidth();
-				drawRc.left += width / 4;
-				drawRc.right -= width / 4;
-
-				// Create two target rectangle offset to the sides of the backbuffer
-				TargetRectangle leftRc = drawRc, rightRc = drawRc;
-				leftRc.left -= s_backbuffer_width / 4;
-				leftRc.right -= s_backbuffer_width / 4;
-				rightRc.left += s_backbuffer_width / 4;
-				rightRc.right += s_backbuffer_width / 4;
+				TargetRectangle leftRc, rightRc;
+				ConvertStereoRectangle(flipped_trc, leftRc, rightRc);
 
 				m_post_processor->BlitFromTexture(sourceRc, leftRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 0);
 				m_post_processor->BlitFromTexture(sourceRc, rightRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 1);
@@ -1544,19 +1535,10 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 		// for msaa mode, we must resolve the efb content to non-msaa
 		GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(rc);
 
-		if (g_ActiveConfig.iStereoMode == STEREO_SBS)
+		if (g_ActiveConfig.iStereoMode == STEREO_SBS || g_ActiveConfig.iStereoMode == STEREO_TAB)
 		{
-			// Resize target to half its original size
-			int width = flipped_trc.GetWidth();
-			flipped_trc.left += width / 4;
-			flipped_trc.right -= width / 4;
-
-			// Create two target rectangle offset to the sides of the backbuffer
-			TargetRectangle leftRc = flipped_trc, rightRc = flipped_trc;
-			leftRc.left -= s_backbuffer_width / 4;
-			leftRc.right -= s_backbuffer_width / 4;
-			rightRc.left += s_backbuffer_width / 4;
-			rightRc.right += s_backbuffer_width / 4;
+			TargetRectangle leftRc, rightRc;
+			ConvertStereoRectangle(flipped_trc, leftRc, rightRc);
 
 			m_post_processor->BlitFromTexture(targetRc, leftRc, tex, s_target_width, s_target_height, 0);
 			m_post_processor->BlitFromTexture(targetRc, rightRc, tex, s_target_width, s_target_height, 1);
diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp
index d93d43f490..84c037c753 100644
--- a/Source/Core/VideoCommon/RenderBase.cpp
+++ b/Source/Core/VideoCommon/RenderBase.cpp
@@ -240,6 +240,42 @@ bool Renderer::CalculateTargetSize(unsigned int framebuffer_width, unsigned int
 	return false;
 }
 
+void Renderer::ConvertStereoRectangle(const TargetRectangle& rc, TargetRectangle& leftRc, TargetRectangle& rightRc)
+{
+	// Resize target to half its original size
+	TargetRectangle drawRc = rc;
+	if (g_ActiveConfig.iStereoMode == STEREO_TAB)
+	{
+		// The height may be negative due to flipped rectangles
+		int height = rc.bottom - rc.top;
+		drawRc.top += height / 4;
+		drawRc.bottom -= height / 4;
+	}
+	else
+	{
+		int width = rc.right - rc.left;
+		drawRc.left += width / 4;
+		drawRc.right -= width / 4;
+	}
+
+	// Create two target rectangle offset to the sides of the backbuffer
+	leftRc = drawRc, rightRc = drawRc;
+	if (g_ActiveConfig.iStereoMode == STEREO_TAB)
+	{
+		leftRc.top -= s_backbuffer_height / 4;
+		leftRc.bottom -= s_backbuffer_height / 4;
+		rightRc.top += s_backbuffer_height / 4;
+		rightRc.bottom += s_backbuffer_height / 4;
+	}
+	else
+	{
+		leftRc.left -= s_backbuffer_width / 4;
+		leftRc.right -= s_backbuffer_width / 4;
+		rightRc.left += s_backbuffer_width / 4;
+		rightRc.right += s_backbuffer_width / 4;
+	}
+}
+
 void Renderer::SetScreenshot(const std::string& filename)
 {
 	std::lock_guard<std::mutex> lk(s_criticalScreenshot);
diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h
index 746f75366c..f59458e523 100644
--- a/Source/Core/VideoCommon/RenderBase.h
+++ b/Source/Core/VideoCommon/RenderBase.h
@@ -83,6 +83,8 @@ public:
 	static const TargetRectangle& GetTargetRectangle() { return target_rc; }
 	static void UpdateDrawRectangle(int backbuffer_width, int backbuffer_height);
 
+	// Use this to convert a single target rectangle to two stereo rectangles
+	static void ConvertStereoRectangle(const TargetRectangle& rc, TargetRectangle& leftRc, TargetRectangle& rightRc);
 
 	// Use this to upscale native EFB coordinates to IDEAL internal resolution
 	static int EFBToScaledX(int x);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index a03e70bdc6..994264e58e 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -48,6 +48,7 @@ enum StereoMode
 {
 	STEREO_OFF = 0,
 	STEREO_SBS,
+	STEREO_TAB,
 	STEREO_ANAGLYPH
 };
 

From aa57feb9a830e1e6b6b398615d05c27e56803be1 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 16:51:37 +0100
Subject: [PATCH 32/66] ProgramShaderCache: Don't call glAttachShader if no
 geometry shader was compiled.

---
 Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
index 69dbbac063..9bbdff6d2d 100644
--- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
+++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
@@ -264,7 +264,8 @@ bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const
 
 	glAttachShader(pid, vsid);
 	glAttachShader(pid, psid);
-	glAttachShader(pid, gsid);
+	if (gsid)
+		glAttachShader(pid, gsid);
 
 	if (g_ogl_config.bSupportsGLSLCache)
 		glProgramParameteri(pid, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);

From c0a5ae17465ad2a575585a2647763607e9e0c062 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 17:05:00 +0100
Subject: [PATCH 33/66] OGL: Also redefine glFramebufferTexture on OpenGL ES.

---
 Source/Core/VideoBackends/OGL/Render.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index ff6f9f25df..2f632563af 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -468,7 +468,7 @@ Renderer::Renderer()
 		glClearDepthf = ClearDepthf;
 	}
 
-	if (GLExtensions::Version() < 320)
+	if (GLExtensions::Version() < 320 || GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
 	{
 		glFramebufferTexture = FramebufferTexture;
 	}

From 2cb22909108246672e63ff947c678f4d8e80a33f Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 17:29:41 +0100
Subject: [PATCH 34/66] TextureCache: Fix invalid cast.

---
 Source/Core/VideoBackends/OGL/TextureCache.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index f58a71fc92..7fd59d5d43 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -380,8 +380,7 @@ TextureCache::TextureCache()
 		"void main()\n"
 		"{\n"
 		"	vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
-		"	f_uv0.xy = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy);\n"
-		"	f_uv0.z = 0;\n"
+		"	f_uv0 = vec3(mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy), 0.0);\n"
 		"	gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
 		"}\n";
 

From 6b2261deca0e66ffb8250ae23e243d1952b39d67 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 17:55:46 +0100
Subject: [PATCH 35/66] VideoConfigDiag: Update descriptions of stereoscopy
 settings.

---
 Source/Core/DolphinWX/VideoConfigDiag.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index 5e4dda014d..feccb6abd9 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -149,9 +149,9 @@ static wxString crop_desc = wxTRANSLATE("Crop the picture from 4:3 to 5:4 or fro
 static wxString ppshader_desc = wxTRANSLATE("Apply a post-processing effect after finishing a frame.\n\nIf unsure, select (off).");
 static wxString cache_efb_copies_desc = wxTRANSLATE("Slightly speeds up EFB to RAM copies by sacrificing emulation accuracy.\nSometimes also increases visual quality.\nIf you're experiencing any issues, try raising texture cache accuracy or disable this option.\n\nIf unsure, leave this unchecked.");
 static wxString shader_errors_desc = wxTRANSLATE("Usually if shader compilation fails, an error message is displayed.\nHowever, one may skip the popups to allow interruption free gameplay by checking this option.\n\nIf unsure, leave this unchecked.");
-static wxString stereo_3d_desc = wxTRANSLATE("Side-by-side stereoscopic 3D.");
-static wxString stereo_separation_desc = wxTRANSLATE("Control the Interpupillary distance.");
-static wxString stereo_focal_desc = wxTRANSLATE("Control the distance at which objects appear to be at screen depth.");
+static wxString stereo_3d_desc = wxTRANSLATE("Select the stereoscopic 3D  mode, stereoscopy allows you to get a better feeling of depth if you have the necessary hardware.\nSide-by-Side and Top-and-Bottom are used by most 3D TVs.\nAnaglyph is used for Red-Cyan colored glasses.\nHeavily decreases emulation speed and sometimes causes issues.\n\nIf unsure, select Off.");
+static wxString stereo_separation_desc = wxTRANSLATE("Control the Interpupillary distance, this is the distance between your eyes measured in millimeters.");
+static wxString stereo_focal_desc = wxTRANSLATE("Control the focal length, this is the distance at which objects appear to be on the screen.  A lower value creates a stronger feeling of depth while a higher value is generally more comfortable.");
 static wxString stereo_swap_desc = wxTRANSLATE("Swap the left and right eye.\n\nIf unsure, leave this unchecked.");
 
 
@@ -451,10 +451,10 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 
 	if (vconfig.backend_info.bSupportsStereoscopy)
 	{
-		wxGridSizer* const szr_stereo = new wxGridSizer(2, 5, 5);
+		wxFlexGridSizer* const szr_stereo = new wxFlexGridSizer(2, 5, 5);
 
 		const wxString stereo_choices[] = { "Off", "Side-by-Side", "Top-and-Bottom", "Anaglyph" };
-		szr_stereo->Add(new wxStaticText(page_enh, -1, _("Stereo 3D Mode:")), 1, wxALIGN_CENTER_VERTICAL, 0);
+		szr_stereo->Add(new wxStaticText(page_enh, -1, _("Stereoscopic 3D Mode:")), 1, wxALIGN_CENTER_VERTICAL, 0);
 		szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 4, stereo_choices));
 
 		wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 30, 90, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
@@ -462,14 +462,14 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 		RegisterControl(sep_slider, wxGetTranslation(stereo_separation_desc));
 
 		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Interpupillary distance:")), 1, wxALIGN_CENTER_VERTICAL, 0);
-		szr_stereo->Add(sep_slider, 1, wxEXPAND | wxRIGHT);
+		szr_stereo->Add(sep_slider, 0, wxEXPAND | wxRIGHT);
 
 		wxSlider* const foc_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoFocalLength, 10, 200, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
 		foc_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoFoc, this);
 		RegisterControl(foc_slider, wxGetTranslation(stereo_focal_desc));
 
 		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Focal Length:")), 1, wxALIGN_CENTER_VERTICAL, 0);
-		szr_stereo->Add(foc_slider, 1, wxEXPAND | wxRIGHT);
+		szr_stereo->Add(foc_slider, 0, wxEXPAND | wxRIGHT);
 
 		szr_stereo->Add(CreateCheckBox(page_enh, _("Swap eyes"), wxGetTranslation(stereo_swap_desc), vconfig.bStereoSwapEyes));
 

From 3baa01fc73d33443dfd96ba34e60ca054f660a0d Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 21:07:03 +0100
Subject: [PATCH 36/66] VideoConfig: Use a smaller focal length default.

---
 Source/Core/VideoCommon/VideoConfig.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index 4bcb2051f0..18f10b0fd2 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -68,7 +68,7 @@ void VideoConfig::Load(const std::string& ini_file)
 	settings->Get("UseFFV1", &bUseFFV1, 0);
 	settings->Get("StereoMode", &iStereoMode, 0);
 	settings->Get("StereoSeparation", &iStereoSeparation, 65);
-	settings->Get("StereoFocalLength", &iStereoFocalLength, 100);
+	settings->Get("StereoFocalLength", &iStereoFocalLength, 50);
 	settings->Get("StereoSwapEyes", &bStereoSwapEyes, false);
 	settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
 	settings->Get("FastDepthCalc", &bFastDepthCalc, true);

From 081212b765754ae80b7a0774ec340c81a47c9199 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Fri, 31 Oct 2014 22:27:18 +0100
Subject: [PATCH 37/66] TextureCache: Force mono copies of the depth buffer.

Fixes stereoscopic stencil shadows in some games.
---
 Source/Core/VideoBackends/OGL/TextureCache.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index 7fd59d5d43..3c2661b043 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -338,7 +338,7 @@ TextureCache::TextureCache()
 		"out vec4 ocol0;\n"
 		"\n"
 		"void main(){\n"
-		"	vec4 texcol = texture(samp9, f_uv0);\n"
+		"	vec4 texcol = texture(samp9, vec3(f_uv0.xy, 0));\n"
 
 		// 255.99998474121 = 16777215/16777216*256
 		"	float workspace = texcol.x * 255.99998474121;\n"

From ab76cf8b5e27eee916107e3f6ddf209407ce169d Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sat, 1 Nov 2014 23:01:22 +0100
Subject: [PATCH 38/66] PostProcessing: Apply color correction to the anaglyph
 shader.

The eyes were accidentally swapped, the left filter only allows red to pass so the left eye texture should be used in the red channel.
---
 Source/Core/VideoBackends/OGL/PostProcessing.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.cpp b/Source/Core/VideoBackends/OGL/PostProcessing.cpp
index 905531226c..84088459ed 100644
--- a/Source/Core/VideoBackends/OGL/PostProcessing.cpp
+++ b/Source/Core/VideoBackends/OGL/PostProcessing.cpp
@@ -171,7 +171,7 @@ void OpenGLPostProcessing::ApplyShader()
 	std::string default_shader = "void main() { SetOutput(Sample()); }\n";
 
 	if (g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH)
-		code = "void main() { SetOutput(float4(SampleLayer(1).r, SampleLayer(0).gba)); }\n";
+		code = "void main() { SetOutput(float4(pow(0.7 * SampleLayer(0).g + 0.3 * SampleLayer(0).b, 1.5), SampleLayer(1).gba)); }\n";
 	else if (g_ActiveConfig.sPostProcessingShader != "")
 		code = m_config.LoadShader();
 

From 940c628eb7587d7f7ab82914ec10094f435d74c6 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sat, 1 Nov 2014 23:02:04 +0100
Subject: [PATCH 39/66] Shaders: Remove fake stereoscopic shaders.

We have actual stereoscopy now, no need to pretend.
---
 Data/Sys/Shaders/stereoscopic.glsl  | 24 ------------------------
 Data/Sys/Shaders/stereoscopic2.glsl | 24 ------------------------
 2 files changed, 48 deletions(-)
 delete mode 100644 Data/Sys/Shaders/stereoscopic.glsl
 delete mode 100644 Data/Sys/Shaders/stereoscopic2.glsl

diff --git a/Data/Sys/Shaders/stereoscopic.glsl b/Data/Sys/Shaders/stereoscopic.glsl
deleted file mode 100644
index 0c49b55f32..0000000000
--- a/Data/Sys/Shaders/stereoscopic.glsl
+++ /dev/null
@@ -1,24 +0,0 @@
-// Omega's 3D Stereoscopic filtering
-// TODO: Need depth info!
-
-void main()
-{
-	// Source Color
-	float4 c0 = Sample();
-	const int sep = 5;
-	float red     = c0.r;
-	float green   = c0.g;
-	float blue    = c0.b;
-
-	// Left Eye (Red)
-	float4 c1 = SampleOffset(int2(sep, 0));
-	red = max(c0.r, c1.r);
-
-	// Right Eye (Cyan)
-	float4 c2 = SampleOffset(int2(-sep, 0));
-	float cyan = (c2.g + c2.b) / 2.0;
-	green = max(c0.g, cyan);
-	blue = max(c0.b, cyan);
-
-	SetOutput(float4(red, green, blue, c0.a));
-}
diff --git a/Data/Sys/Shaders/stereoscopic2.glsl b/Data/Sys/Shaders/stereoscopic2.glsl
deleted file mode 100644
index 1784a988bf..0000000000
--- a/Data/Sys/Shaders/stereoscopic2.glsl
+++ /dev/null
@@ -1,24 +0,0 @@
-// Omega's 3D Stereoscopic filtering (Amber/Blue)
-// TODO: Need depth info!
-
-void main()
-{
-	// Source Color
-	float4 c0 = Sample();
-	float sep = 5.0;
-	float red   = c0.r;
-	float green = c0.g;
-	float blue  = c0.b;
-
-	// Left Eye (Amber)
-	float4 c2 = SampleLocation(GetCoordinates() + float2(sep,0.0)*GetInvResolution()).rgba;
-	float amber = (c2.r + c2.g) / 2.0;
-	red = max(c0.r, amber);
-	green = max(c0.g, amber);
-
-	// Right Eye (Blue)
-	float4 c1 = SampleLocation(GetCoordinates() + float2(-sep,0.0)*GetInvResolution()).rgba;
-	blue = max(c0.b, c1.b);
-
-	SetOutput(float4(red, green, blue, c0.a));
-}

From acc65ee60807d7ec605f85bc101a4dc9a9a63e32 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sat, 1 Nov 2014 23:55:04 +0100
Subject: [PATCH 40/66] GeometryShader: Replicate missing position
 manipulations from vertex shader.

---
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index 57fc4b7d8b..8260ed5f05 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -86,6 +86,16 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	if (g_ActiveConfig.iStereoMode > 0)
 		out.Write("\t\to.pos = float4(dot(stereoproj, v[i].rawpos), dot(" I_PROJECTION"[1], v[i].rawpos), dot(" I_PROJECTION"[2], v[i].rawpos), dot(" I_PROJECTION"[3], v[i].rawpos)); \n");
 
+	if (ApiType == API_D3D)
+	{
+		out.Write("\t\to.pos.z = " I_DEPTHPARAMS".x * o.pos.w + o.pos.z * " I_DEPTHPARAMS".y;\n");
+	}
+	else // OGL
+	{
+		out.Write("\t\to.pos.z = o.pos.w + o.pos.z * 2.0;\n");
+	}
+
+	out.Write("\t\to.pos.xy = o.pos.xy - " I_DEPTHPARAMS".zw;\n");
 	out.Write("\t\tgl_Position = o.pos;\n");
 	out.Write("\t\tEmitVertex();\n");
 	out.Write("\t}\n");

From 6642af2404656c987d58e26833de883ea8365a0c Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 4 Nov 2014 00:44:30 +0100
Subject: [PATCH 41/66] OGL: Remove Virtual XFB SBS support.

---
 Source/Core/VideoBackends/OGL/Render.cpp | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 2f632563af..4432a3e8d7 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -582,7 +582,7 @@ Renderer::Renderer()
 				g_ogl_config.gl_renderer,
 				g_ogl_config.gl_version), 5000);
 
-	WARN_LOG(VIDEO,"Missing OGL Extensions: %s%s%s%s%s%s%s%s%s%s",
+	WARN_LOG(VIDEO,"Missing OGL Extensions: %s%s%s%s%s%s%s%s%s%s%s",
 			g_ActiveConfig.backend_info.bSupportsDualSourceBlend ? "" : "DualSourceBlend ",
 			g_ActiveConfig.backend_info.bSupportsPrimitiveRestart ? "" : "PrimitiveRestart ",
 			g_ActiveConfig.backend_info.bSupportsEarlyZ ? "" : "EarlyZ ",
@@ -1514,18 +1514,8 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
 
 			sourceRc.right -= fbStride - fbWidth;
 
-			if (g_ActiveConfig.iStereoMode == STEREO_SBS || g_ActiveConfig.iStereoMode == STEREO_TAB)
-			{
-				TargetRectangle leftRc, rightRc;
-				ConvertStereoRectangle(flipped_trc, leftRc, rightRc);
-
-				m_post_processor->BlitFromTexture(sourceRc, leftRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 0);
-				m_post_processor->BlitFromTexture(sourceRc, rightRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 1);
-			}
-			else
-			{
-				m_post_processor->BlitFromTexture(sourceRc, drawRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 1);
-			}
+			// TODO: Virtual XFB stereoscopic 3D support.
+			m_post_processor->BlitFromTexture(sourceRc, drawRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight, 1);
 		}
 	}
 	else

From 4d075c2efb4c1dda695cd8044b42cf7894b52e5a Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 4 Nov 2014 00:46:43 +0100
Subject: [PATCH 42/66] ProgramShaderCache: Abort shader compilation if
 geometry shader failed to compile.

---
 Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
index 9bbdff6d2d..bf789f46a6 100644
--- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
+++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
@@ -252,7 +252,7 @@ bool ProgramShaderCache::CompileShader(SHADER& shader, const char* vcode, const
 	if (gcode)
 		gsid = CompileSingleShader(GL_GEOMETRY_SHADER, gcode);
 
-	if (!vsid || !psid)
+	if (!vsid || !psid || (gcode && !gsid))
 	{
 		glDeleteShader(vsid);
 		glDeleteShader(psid);

From ee76c03160b72731facbce135be26d7ef8cebba5 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 4 Nov 2014 00:53:14 +0100
Subject: [PATCH 43/66] TextureCache: Recompile EFB2Tex shaders when stereo 3D
 is toggled.

---
 Source/Core/VideoBackends/D3D/TextureCache.h  |  3 ++
 .../Core/VideoBackends/OGL/TextureCache.cpp   | 44 ++++++++++++-------
 Source/Core/VideoBackends/OGL/TextureCache.h  |  3 ++
 Source/Core/VideoCommon/TextureCacheBase.cpp  |  7 +++
 Source/Core/VideoCommon/TextureCacheBase.h    |  4 ++
 5 files changed, 44 insertions(+), 17 deletions(-)

diff --git a/Source/Core/VideoBackends/D3D/TextureCache.h b/Source/Core/VideoBackends/D3D/TextureCache.h
index 6045d3f7db..79cd8145a8 100644
--- a/Source/Core/VideoBackends/D3D/TextureCache.h
+++ b/Source/Core/VideoBackends/D3D/TextureCache.h
@@ -43,6 +43,9 @@ private:
 
 	TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) override;
 	u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 SourceW, u32 SourceH, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) {return 0;};
+
+	void CompileShaders() override { }
+	void DeleteShaders() override { }
 };
 
 }
diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index 3c2661b043..547a527818 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -318,6 +318,32 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo
 }
 
 TextureCache::TextureCache()
+{
+	CompileShaders();
+
+	s_ActiveTexture = -1;
+	for (auto& gtex : s_Textures)
+		gtex = -1;
+}
+
+
+TextureCache::~TextureCache()
+{
+	DeleteShaders();
+}
+
+void TextureCache::DisableStage(unsigned int stage)
+{
+}
+
+void TextureCache::SetStage()
+{
+	// -1 is the initial value as we don't know which texture should be bound
+	if (s_ActiveTexture != (u32)-1)
+		glActiveTexture(GL_TEXTURE0 + s_ActiveTexture);
+}
+
+void TextureCache::CompileShaders()
 {
 	const char *pColorMatrixProg =
 		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
@@ -414,28 +440,12 @@ TextureCache::TextureCache()
 
 	s_ColorCopyPositionUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "copy_position");
 	s_DepthCopyPositionUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "copy_position");
-
-	s_ActiveTexture = -1;
-	for (auto& gtex : s_Textures)
-		gtex = -1;
 }
 
-
-TextureCache::~TextureCache()
+void TextureCache::DeleteShaders()
 {
 	s_ColorMatrixProgram.Destroy();
 	s_DepthMatrixProgram.Destroy();
 }
 
-void TextureCache::DisableStage(unsigned int stage)
-{
-}
-
-void TextureCache::SetStage ()
-{
-	// -1 is the initial value as we don't know which texture should be bound
-	if (s_ActiveTexture != (u32)-1)
-		glActiveTexture(GL_TEXTURE0 + s_ActiveTexture);
-}
-
 }
diff --git a/Source/Core/VideoBackends/OGL/TextureCache.h b/Source/Core/VideoBackends/OGL/TextureCache.h
index e1e2ec694c..f8d181e8e1 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.h
+++ b/Source/Core/VideoBackends/OGL/TextureCache.h
@@ -57,6 +57,9 @@ private:
 		unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) override;
 
 	TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) override;
+
+	void CompileShaders() override;
+	void DeleteShaders() override;
 };
 
 bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width, int virtual_height, unsigned int level);
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index 4037a7470b..651bb90708 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -115,6 +115,12 @@ void TextureCache::OnConfigChanged(VideoConfig& config)
 		{
 			g_texture_cache->ClearRenderTargets();
 		}
+
+		if ((config.iStereoMode > 0) != backup_config.s_stereo_3d)
+		{
+			g_texture_cache->DeleteShaders();
+			g_texture_cache->CompileShaders();
+		}
 	}
 
 	backup_config.s_colorsamples = config.iSafeTextureCache_ColorSamples;
@@ -126,6 +132,7 @@ void TextureCache::OnConfigChanged(VideoConfig& config)
 	backup_config.s_texfmt_overlay_center = config.bTexFmtOverlayCenter;
 	backup_config.s_hires_textures = config.bHiresTextures;
 	backup_config.s_copy_cache_enable = config.bEFBCopyCacheEnable;
+	backup_config.s_stereo_3d = config.iStereoMode > 0;
 }
 
 void TextureCache::Cleanup()
diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h
index 77bdec14fa..7fc3e49a2f 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.h
+++ b/Source/Core/VideoCommon/TextureCacheBase.h
@@ -102,6 +102,9 @@ public:
 		unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) = 0;
 	virtual TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) = 0;
 
+	virtual void CompileShaders() = 0; // currently only implemented by OGL
+	virtual void DeleteShaders() = 0; // currently only implemented by OGL
+
 	static TCacheEntryBase* Load(unsigned int stage, u32 address, unsigned int width, unsigned int height,
 		int format, unsigned int tlutaddr, int tlutfmt, bool use_mipmaps, unsigned int maxlevel, bool from_tmem);
 	static void CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, PEControl::PixelFormat srcFormat,
@@ -141,6 +144,7 @@ private:
 		bool s_texfmt_overlay_center;
 		bool s_hires_textures;
 		bool s_copy_cache_enable;
+		bool s_stereo_3d;
 	} backup_config;
 };
 

From 51a4d6a4bef09e7b270ee26e5b2889379819da45 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 4 Nov 2014 01:12:00 +0100
Subject: [PATCH 44/66] GeometryShader: Adjust positions after projection.

By adjusting the positions in clip space we can avoid the re-projection.
---
 Source/Core/VideoCommon/GeometryShaderGen.cpp   | 13 ++-----------
 Source/Core/VideoCommon/VertexShaderGen.cpp     |  8 +-------
 Source/Core/VideoCommon/VertexShaderManager.cpp |  4 ++--
 3 files changed, 5 insertions(+), 20 deletions(-)

diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index 8260ed5f05..d116dfe39a 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -77,25 +77,16 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 		out.Write("\tfor (layer = 0; layer < %d; ++layer) {\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1);
 
 	out.Write("\tgl_Layer = layer;\n");
-	out.Write("\tvec4 stereoproj = " I_PROJECTION"[0];\n");
-	out.Write("\tstereoproj[2] += " I_STEREOOFFSET"[layer] * stereoproj[0];\n");
 
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
 	out.Write("\t\to = v[i];\n");
 
 	if (g_ActiveConfig.iStereoMode > 0)
-		out.Write("\t\to.pos = float4(dot(stereoproj, v[i].rawpos), dot(" I_PROJECTION"[1], v[i].rawpos), dot(" I_PROJECTION"[2], v[i].rawpos), dot(" I_PROJECTION"[3], v[i].rawpos)); \n");
-
-	if (ApiType == API_D3D)
 	{
-		out.Write("\t\to.pos.z = " I_DEPTHPARAMS".x * o.pos.w + o.pos.z * " I_DEPTHPARAMS".y;\n");
-	}
-	else // OGL
-	{
-		out.Write("\t\to.pos.z = o.pos.w + o.pos.z * 2.0;\n");
+		out.Write("\t\to.clipPos.x += o.clipPos.w * " I_STEREOOFFSET"[layer] * " I_PROJECTION"[0][0];\n");
+		out.Write("\t\to.pos.x += o.pos.w * " I_STEREOOFFSET"[layer] * " I_PROJECTION"[0][0];\n");
 	}
 
-	out.Write("\t\to.pos.xy = o.pos.xy - " I_DEPTHPARAMS".zw;\n");
 	out.Write("\t\tgl_Position = o.pos;\n");
 	out.Write("\t\tEmitVertex();\n");
 	out.Write("\t}\n");
diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp
index fd2ee7f3e6..700bd0d2a6 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.cpp
+++ b/Source/Core/VideoCommon/VertexShaderGen.cpp
@@ -52,9 +52,6 @@ static inline void GenerateVSOutputStruct(T& object, API_TYPE api_type)
 	if (g_ActiveConfig.bEnablePixelLighting)
 		DefineVSOutputStructMember(object, api_type, "float4", "Normal", -1, "TEXCOORD", xfmem.numTexGen.numTexGens + 1);
 
-	if (g_ActiveConfig.iStereoMode > 0)
-		DefineVSOutputStructMember(object, api_type, "float4", "rawpos", -1, "POSITION");
-
 	object.Write("};\n");
 }
 
@@ -125,6 +122,7 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 				out.Write("in float%d tex%d; // ATTR%d,\n", hastexmtx ? 3 : 2, i, SHADER_TEXTURE0_ATTRIB + i);
 		}
 
+		uid_data->stereo = g_ActiveConfig.iStereoMode > 0;
 		out.Write("centroid out VS_OUTPUT %s;\n", (g_ActiveConfig.iStereoMode > 0) ? "v" : "o");
 
 		out.Write("void main()\n{\n");
@@ -205,10 +203,6 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 
 	out.Write("o.pos = float4(dot(" I_PROJECTION"[0], pos), dot(" I_PROJECTION"[1], pos), dot(" I_PROJECTION"[2], pos), dot(" I_PROJECTION"[3], pos));\n");
 
-	uid_data->stereo = g_ActiveConfig.iStereoMode > 0;
-	if (g_ActiveConfig.iStereoMode > 0)
-		out.Write("o.rawpos = pos;\n");
-
 	out.Write("int4 lacc;\n"
 			"float3 ldir, h;\n"
 			"float dist, dist2, attn;\n");
diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp
index 150bd783a8..f6d285cffd 100644
--- a/Source/Core/VideoCommon/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexShaderManager.cpp
@@ -516,8 +516,8 @@ void VertexShaderManager::SetConstants()
 		if (g_ActiveConfig.iStereoMode > 0 && xfmem.projection.type == GX_PERSPECTIVE)
 		{
 			float offset = g_ActiveConfig.iStereoSeparation / (200.0f * g_ActiveConfig.iStereoFocalLength);
-			constants.stereooffset[0] = (g_ActiveConfig.bStereoSwapEyes) ? -offset : offset;
-			constants.stereooffset[1] = (g_ActiveConfig.bStereoSwapEyes) ? offset : -offset;
+			constants.stereooffset[0] = (g_ActiveConfig.bStereoSwapEyes) ? offset : -offset;
+			constants.stereooffset[1] = (g_ActiveConfig.bStereoSwapEyes) ? -offset : offset;
 		}
 		else
 		{

From 27f3f804a0fd002a19143b2373a6c622fcb56589 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 4 Nov 2014 11:53:19 +0100
Subject: [PATCH 45/66] ShaderGen: Only pass VS_OUTPUT between shaders if
 stereo 3D is enabled.

GLSL130 doesn't support passing structs between shaders.
This is not a problem for stereo 3D which has a GLSL150 requirement.
---
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 12 ++---
 Source/Core/VideoCommon/PixelShaderGen.cpp    | 47 ++++++++++++++-----
 Source/Core/VideoCommon/VertexShaderGen.cpp   | 44 +++++++++++++++--
 3 files changed, 80 insertions(+), 23 deletions(-)

diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index d116dfe39a..bac3823747 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -64,8 +64,8 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 
 	GenerateVSOutputStruct(out, ApiType);
 
-	out.Write("centroid in VS_OUTPUT v[];\n");
-	out.Write("centroid out VS_OUTPUT o;\n");
+	out.Write("centroid in VS_OUTPUT o[];\n");
+	out.Write("centroid out VS_OUTPUT f;\n");
 
 	out.Write("flat out int layer;\n");
 
@@ -79,15 +79,15 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write("\tgl_Layer = layer;\n");
 
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
-	out.Write("\t\to = v[i];\n");
+	out.Write("\t\tf = o[i];\n");
 
 	if (g_ActiveConfig.iStereoMode > 0)
 	{
-		out.Write("\t\to.clipPos.x += o.clipPos.w * " I_STEREOOFFSET"[layer] * " I_PROJECTION"[0][0];\n");
-		out.Write("\t\to.pos.x += o.pos.w * " I_STEREOOFFSET"[layer] * " I_PROJECTION"[0][0];\n");
+		out.Write("\t\tf.clipPos.x += f.clipPos.w * " I_STEREOOFFSET"[layer] * " I_PROJECTION"[0][0];\n");
+		out.Write("\t\tf.pos.x += f.pos.w * " I_STEREOOFFSET"[layer] * " I_PROJECTION"[0][0];\n");
 	}
 
-	out.Write("\t\tgl_Position = o.pos;\n");
+	out.Write("\t\tgl_Position = f.pos;\n");
 	out.Write("\t\tEmitVertex();\n");
 	out.Write("\t}\n");
 	out.Write("\tEndPrimitive();\n");
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index 11c9679ca9..71e534df0a 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -319,30 +319,51 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T
 		// As a workaround, we interpolate at the centroid of the coveraged pixel, which
 		// is always inside the primitive.
 		// Without MSAA, this flag is defined to have no effect.
-		out.Write("centroid in VS_OUTPUT o;\n");
-
 		uid_data->stereo = g_ActiveConfig.iStereoMode > 0;
 		if (g_ActiveConfig.iStereoMode > 0)
+		{
+			out.Write("centroid in VS_OUTPUT f;\n");
 			out.Write("flat in int layer;\n");
+		}
+		else
+		{
+			out.Write("centroid in float4 colors_02;\n");
+			out.Write("centroid in float4 colors_12;\n");
+
+			// compute window position if needed because binding semantic WPOS is not widely supported
+			// Let's set up attributes
+			for (unsigned int i = 0; i < numTexgen; ++i)
+			{
+				out.Write("centroid in float3 uv%d;\n", i);
+			}
+			out.Write("centroid in float4 clipPos;\n");
+			if (g_ActiveConfig.bEnablePixelLighting)
+			{
+				out.Write("centroid in float4 Normal;\n");
+			}
+		}
 
 		out.Write("void main()\n{\n");
 
-		// compute window position if needed because binding semantic WPOS is not widely supported
-		// Let's set up attributes
-		for (unsigned int i = 0; i < numTexgen; ++i)
+		if (g_ActiveConfig.iStereoMode > 0)
 		{
-			out.Write("\tfloat3 uv%d = o.tex%d;\n", i, i);
-		}
-		out.Write("\tfloat4 clipPos = o.clipPos;\n");
-		if (g_ActiveConfig.bEnablePixelLighting)
-		{
-			out.Write("\tfloat4 Normal = o.Normal;\n");
+			// compute window position if needed because binding semantic WPOS is not widely supported
+			// Let's set up attributes
+			for (unsigned int i = 0; i < numTexgen; ++i)
+			{
+				out.Write("\tfloat3 uv%d = f.tex%d;\n", i, i);
+			}
+			out.Write("\tfloat4 clipPos = f.clipPos;\n");
+			if (g_ActiveConfig.bEnablePixelLighting)
+			{
+				out.Write("\tfloat4 Normal = f.Normal;\n");
+			}
 		}
 
 		// On Mali, global variables must be initialized as constants.
 		// This is why we initialize these variables locally instead.
-		out.Write("\tfloat4 colors_0 = o.colors_0;\n");
-		out.Write("\tfloat4 colors_1 = o.colors_1;\n");
+		out.Write("\tfloat4 colors_0 = %s;\n", (g_ActiveConfig.iStereoMode > 0) ? "f.colors_0" : "colors_02");
+		out.Write("\tfloat4 colors_1 = %s;\n", (g_ActiveConfig.iStereoMode > 0) ? "f.colors_1" : "colors_12");
 
 		out.Write("\tfloat4 rawpos = gl_FragCoord;\n");
 	}
diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp
index 700bd0d2a6..24c6f39f46 100644
--- a/Source/Core/VideoCommon/VertexShaderGen.cpp
+++ b/Source/Core/VideoCommon/VertexShaderGen.cpp
@@ -123,11 +123,31 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 		}
 
 		uid_data->stereo = g_ActiveConfig.iStereoMode > 0;
-		out.Write("centroid out VS_OUTPUT %s;\n", (g_ActiveConfig.iStereoMode > 0) ? "v" : "o");
+		if (g_ActiveConfig.iStereoMode > 0)
+		{
+			out.Write("centroid out VS_OUTPUT o;\n");
+		}
+		else
+		{
+			// Let's set up attributes
+			for (size_t i = 0; i < 8; ++i)
+			{
+				if (i < xfmem.numTexGen.numTexGens)
+				{
+					out.Write("centroid out float3 uv%d;\n", i);
+				}
+			}
+
+			out.Write("centroid out float4 clipPos;\n");
+			if (g_ActiveConfig.bEnablePixelLighting)
+				out.Write("centroid out float4 Normal;\n");
+			out.Write("centroid out float4 colors_02;\n");
+			out.Write("centroid out float4 colors_12;\n");
+		}
 
 		out.Write("void main()\n{\n");
 
-		if (g_ActiveConfig.iStereoMode > 0)
+		if (g_ActiveConfig.iStereoMode <= 0)
 			out.Write("VS_OUTPUT o;\n");
 	}
 	else // D3D
@@ -414,8 +434,24 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ
 
 	if (api_type == API_OPENGL)
 	{
-		if (g_ActiveConfig.iStereoMode > 0)
-			out.Write("v = o;\n");
+		if (g_ActiveConfig.iStereoMode <= 0)
+		{
+			// Bit ugly here
+			// TODO: Make pretty
+			// Will look better when we bind uniforms in GLSL 1.3
+			// clipPos/w needs to be done in pixel shader, not here
+
+			for (unsigned int i = 0; i < xfmem.numTexGen.numTexGens; ++i)
+				out.Write("uv%d.xyz = o.tex%d;\n", i, i);
+
+			out.Write("clipPos = o.clipPos;\n");
+
+			if (g_ActiveConfig.bEnablePixelLighting)
+				out.Write("Normal = o.Normal;\n");
+
+			out.Write("colors_02 = o.colors_0;\n");
+			out.Write("colors_12 = o.colors_1;\n");
+		}
 
 		out.Write("gl_Position = o.pos;\n");
 	}

From 02ad1a36ea0c11d5bac2c2d55e2af51b3aa6111f Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Wed, 5 Nov 2014 02:00:37 +0100
Subject: [PATCH 46/66] VideoConfigDiag: Update descriptions.

Since we cannot accurately calculate the eye seperation or the focal length without headtracking these values are purely virtual.
---
 Source/Core/DolphinWX/VideoConfigDiag.cpp | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index feccb6abd9..6d91447ace 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -150,9 +150,9 @@ static wxString ppshader_desc = wxTRANSLATE("Apply a post-processing effect afte
 static wxString cache_efb_copies_desc = wxTRANSLATE("Slightly speeds up EFB to RAM copies by sacrificing emulation accuracy.\nSometimes also increases visual quality.\nIf you're experiencing any issues, try raising texture cache accuracy or disable this option.\n\nIf unsure, leave this unchecked.");
 static wxString shader_errors_desc = wxTRANSLATE("Usually if shader compilation fails, an error message is displayed.\nHowever, one may skip the popups to allow interruption free gameplay by checking this option.\n\nIf unsure, leave this unchecked.");
 static wxString stereo_3d_desc = wxTRANSLATE("Select the stereoscopic 3D  mode, stereoscopy allows you to get a better feeling of depth if you have the necessary hardware.\nSide-by-Side and Top-and-Bottom are used by most 3D TVs.\nAnaglyph is used for Red-Cyan colored glasses.\nHeavily decreases emulation speed and sometimes causes issues.\n\nIf unsure, select Off.");
-static wxString stereo_separation_desc = wxTRANSLATE("Control the Interpupillary distance, this is the distance between your eyes measured in millimeters.");
-static wxString stereo_focal_desc = wxTRANSLATE("Control the focal length, this is the distance at which objects appear to be on the screen.  A lower value creates a stronger feeling of depth while a higher value is generally more comfortable.");
-static wxString stereo_swap_desc = wxTRANSLATE("Swap the left and right eye.\n\nIf unsure, leave this unchecked.");
+static wxString stereo_separation_desc = wxTRANSLATE("Control the interpupillary distance, this is the distance between the virtual eyes.");
+static wxString stereo_convergence_desc = wxTRANSLATE("Control the convergence distance, this controls the apparant distance of virtual objects.\nA lower value creates a stronger feeling of depth while a higher value is generally more comfortable.");
+static wxString stereo_swap_desc = wxTRANSLATE("Swap the left and right eye, mostly useful if you want to view side-by-side cross-eyed.\n\nIf unsure, leave this unchecked.");
 
 
 #if !defined(__APPLE__)
@@ -461,15 +461,15 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 		sep_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoSep, this);
 		RegisterControl(sep_slider, wxGetTranslation(stereo_separation_desc));
 
-		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Interpupillary distance:")), 1, wxALIGN_CENTER_VERTICAL, 0);
+		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Separation:")), 1, wxALIGN_CENTER_VERTICAL, 0);
 		szr_stereo->Add(sep_slider, 0, wxEXPAND | wxRIGHT);
 
-		wxSlider* const foc_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoFocalLength, 10, 200, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
-		foc_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoFoc, this);
-		RegisterControl(foc_slider, wxGetTranslation(stereo_focal_desc));
+		wxSlider* const conv_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoFocalLength, 10, 200, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
+		conv_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoFoc, this);
+		RegisterControl(conv_slider, wxGetTranslation(stereo_convergence_desc));
 
-		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Focal Length:")), 1, wxALIGN_CENTER_VERTICAL, 0);
-		szr_stereo->Add(foc_slider, 0, wxEXPAND | wxRIGHT);
+		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Convergence:")), 1, wxALIGN_CENTER_VERTICAL, 0);
+		szr_stereo->Add(conv_slider, 0, wxEXPAND | wxRIGHT);
 
 		szr_stereo->Add(CreateCheckBox(page_enh, _("Swap eyes"), wxGetTranslation(stereo_swap_desc), vconfig.bStereoSwapEyes));
 

From 8210b9c915816cf8f778db1b633f2327897af005 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 6 Nov 2014 11:41:39 +0100
Subject: [PATCH 47/66] TextureCache: Ensure that all render target textures
 have as many layers as the frame buffer.

Also fixes a case where the D3D code path did not initialize num_layers leading to undefined behaviour.
---
 Source/Core/VideoBackends/OGL/FramebufferManager.cpp | 2 --
 Source/Core/VideoBackends/OGL/FramebufferManager.h   | 4 ----
 Source/Core/VideoBackends/OGL/TextureCache.cpp       | 5 +----
 Source/Core/VideoCommon/FramebufferManagerBase.cpp   | 2 ++
 Source/Core/VideoCommon/FramebufferManagerBase.h     | 4 ++++
 Source/Core/VideoCommon/TextureCacheBase.cpp         | 8 ++++----
 Source/Core/VideoCommon/TextureCacheBase.h           | 3 ++-
 7 files changed, 13 insertions(+), 15 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
index dcc5791dfc..f24172e7e0 100644
--- a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
+++ b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp
@@ -21,8 +21,6 @@ int FramebufferManager::m_targetHeight;
 int FramebufferManager::m_msaaSamples;
 
 GLenum FramebufferManager::m_textureType;
-int FramebufferManager::m_EFBLayers;
-
 GLuint FramebufferManager::m_efbFramebuffer;
 GLuint FramebufferManager::m_xfbFramebuffer;
 GLuint FramebufferManager::m_efbColor;
diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.h b/Source/Core/VideoBackends/OGL/FramebufferManager.h
index bce63a0048..ed129c5a6e 100644
--- a/Source/Core/VideoBackends/OGL/FramebufferManager.h
+++ b/Source/Core/VideoBackends/OGL/FramebufferManager.h
@@ -74,8 +74,6 @@ public:
 	// Resolved framebuffer is only used in MSAA mode.
 	static GLuint GetResolvedFramebuffer() { return m_resolvedFramebuffer; }
 
-	static int GetEFBLayers() { return m_EFBLayers; }
-
 	static void SetFramebuffer(GLuint fb);
 
 	// If in MSAA mode, this will perform a resolve of the specified rectangle, and return the resolve target as a texture ID.
@@ -103,8 +101,6 @@ private:
 	static int m_msaaSamples;
 
 	static GLenum m_textureType;
-	static int m_EFBLayers;
-
 	static GLuint m_efbFramebuffer;
 	static GLuint m_xfbFramebuffer;
 	static GLuint m_efbColor;
diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index 547a527818..a84da5dbb6 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -221,10 +221,7 @@ TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
 		gl_type = GL_UNSIGNED_BYTE;
 
 	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
-
-	entry->num_layers = FramebufferManager::GetEFBLayers();
-
-	glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, gl_iformat, scaled_tex_w, scaled_tex_h, entry->num_layers, 0, gl_format, gl_type, nullptr);
+	glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, gl_iformat, scaled_tex_w, scaled_tex_h, FramebufferManager::GetEFBLayers(), 0, gl_format, gl_type, nullptr);
 	glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
 
 	glGenFramebuffers(1, &entry->framebuffer);
diff --git a/Source/Core/VideoCommon/FramebufferManagerBase.cpp b/Source/Core/VideoCommon/FramebufferManagerBase.cpp
index ea865cd275..e2a09c6527 100644
--- a/Source/Core/VideoCommon/FramebufferManagerBase.cpp
+++ b/Source/Core/VideoCommon/FramebufferManagerBase.cpp
@@ -12,6 +12,8 @@ const XFBSourceBase* FramebufferManagerBase::m_overlappingXFBArray[MAX_VIRTUAL_X
 unsigned int FramebufferManagerBase::s_last_xfb_width = 1;
 unsigned int FramebufferManagerBase::s_last_xfb_height = 1;
 
+unsigned int FramebufferManagerBase::m_EFBLayers = 1;
+
 FramebufferManagerBase::FramebufferManagerBase()
 {
 	m_realXFBSource = nullptr;
diff --git a/Source/Core/VideoCommon/FramebufferManagerBase.h b/Source/Core/VideoCommon/FramebufferManagerBase.h
index 50813539ab..b01e820243 100644
--- a/Source/Core/VideoCommon/FramebufferManagerBase.h
+++ b/Source/Core/VideoCommon/FramebufferManagerBase.h
@@ -55,6 +55,8 @@ public:
 	static int ScaleToVirtualXfbWidth(int x, unsigned int backbuffer_width);
 	static int ScaleToVirtualXfbHeight(int y, unsigned int backbuffer_height);
 
+	static int GetEFBLayers() { return m_EFBLayers; }
+
 protected:
 	struct VirtualXFB
 	{
@@ -70,6 +72,8 @@ protected:
 
 	typedef std::list<VirtualXFB> VirtualXFBListType;
 
+	static unsigned int m_EFBLayers;
+
 private:
 	virtual XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height) = 0;
 	// TODO: figure out why OGL is different for this guy
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index 651bb90708..e057b379ad 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -538,7 +538,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
 		entry->Load(width, height, expandedWidth, 0);
 	}
 
-	entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps);
+	entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps, entry->num_layers);
 	entry->SetDimensions(nativeW, nativeH, width, height);
 	entry->hash = tex_hash;
 
@@ -895,12 +895,12 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
 	TCacheEntryBase *entry = textures[dstAddr];
 	if (entry)
 	{
-		if (entry->type == TCET_EC_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h)
+		if (entry->type == TCET_EC_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h && entry->num_layers == FramebufferManagerBase::GetEFBLayers())
 		{
 			scaled_tex_w = tex_w;
 			scaled_tex_h = tex_h;
 		}
-		else if (!(entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h))
+		else if (!(entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h && entry->num_layers == FramebufferManagerBase::GetEFBLayers()))
 		{
 			if (entry->type == TCET_EC_VRAM)
 			{
@@ -923,7 +923,7 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
 		textures[dstAddr] = entry = AllocateRenderTarget(scaled_tex_w, scaled_tex_h);
 
 		// TODO: Using the wrong dstFormat, dumb...
-		entry->SetGeneralParameters(dstAddr, 0, dstFormat, 1);
+		entry->SetGeneralParameters(dstAddr, 0, dstFormat, 1, FramebufferManagerBase::GetEFBLayers());
 		entry->SetDimensions(tex_w, tex_h, scaled_tex_w, scaled_tex_h);
 		entry->SetHashes(TEXHASH_INVALID);
 		entry->type = TCET_EC_VRAM;
diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h
index 7fc3e49a2f..64bc41f19d 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.h
+++ b/Source/Core/VideoCommon/TextureCacheBase.h
@@ -47,12 +47,13 @@ public:
 		int frameCount;
 
 
-		void SetGeneralParameters(u32 _addr, u32 _size, u32 _format, unsigned int _num_mipmaps)
+		void SetGeneralParameters(u32 _addr, u32 _size, u32 _format, unsigned int _num_mipmaps, unsigned int _num_layers)
 		{
 			addr = _addr;
 			size_in_bytes = _size;
 			format = _format;
 			num_mipmaps = _num_mipmaps;
+			num_layers = _num_layers;
 		}
 
 		void SetDimensions(unsigned int _native_width, unsigned int _native_height, unsigned int _virtual_width, unsigned int _virtual_height)

From f3ddf37d074f80bf40b42995e91db98b4d599534 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sat, 8 Nov 2014 14:31:26 +0100
Subject: [PATCH 48/66] VideoCommon: Switch to Nvidia stereoscopy offset
 formula.

---
 Source/Core/DolphinWX/VideoConfigDiag.cpp       | 2 +-
 Source/Core/VideoCommon/ConstantManager.h       | 2 +-
 Source/Core/VideoCommon/GeometryShaderGen.cpp   | 4 ++--
 Source/Core/VideoCommon/ShaderGenCommon.h       | 4 ++--
 Source/Core/VideoCommon/VertexShaderManager.cpp | 9 +++++----
 Source/Core/VideoCommon/VideoConfig.cpp         | 4 ++--
 6 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index 6d91447ace..dae92a728a 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -457,7 +457,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 		szr_stereo->Add(new wxStaticText(page_enh, -1, _("Stereoscopic 3D Mode:")), 1, wxALIGN_CENTER_VERTICAL, 0);
 		szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 4, stereo_choices));
 
-		wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 30, 90, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
+		wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 10, 100, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
 		sep_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoSep, this);
 		RegisterControl(sep_slider, wxGetTranslation(stereo_separation_desc));
 
diff --git a/Source/Core/VideoCommon/ConstantManager.h b/Source/Core/VideoCommon/ConstantManager.h
index e31806bbd1..116088d76e 100644
--- a/Source/Core/VideoCommon/ConstantManager.h
+++ b/Source/Core/VideoCommon/ConstantManager.h
@@ -43,6 +43,6 @@ struct VertexShaderConstants
 	float4 normalmatrices[32];
 	float4 posttransformmatrices[64];
 	float4 depthparams;
-	float4 stereooffset;
+	float4 stereoparams;
 };
 
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index bac3823747..ff7632da05 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -83,8 +83,8 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 
 	if (g_ActiveConfig.iStereoMode > 0)
 	{
-		out.Write("\t\tf.clipPos.x += f.clipPos.w * " I_STEREOOFFSET"[layer] * " I_PROJECTION"[0][0];\n");
-		out.Write("\t\tf.pos.x += f.pos.w * " I_STEREOOFFSET"[layer] * " I_PROJECTION"[0][0];\n");
+		out.Write("\t\tf.clipPos.x += " I_STEREOPARAMS"[layer] * (f.clipPos.w - " I_STEREOPARAMS"[2]);\n");
+		out.Write("\t\tf.pos.x += " I_STEREOPARAMS"[layer] * (f.pos.w - " I_STEREOPARAMS"[2]);\n");
 	}
 
 	out.Write("\t\tgl_Position = f.pos;\n");
diff --git a/Source/Core/VideoCommon/ShaderGenCommon.h b/Source/Core/VideoCommon/ShaderGenCommon.h
index 5e9e24e7fa..330b9f8ced 100644
--- a/Source/Core/VideoCommon/ShaderGenCommon.h
+++ b/Source/Core/VideoCommon/ShaderGenCommon.h
@@ -239,7 +239,7 @@ private:
 #define I_NORMALMATRICES        "cnmtx"
 #define I_POSTTRANSFORMMATRICES "cpostmtx"
 #define I_DEPTHPARAMS           "cDepth" // farZ, zRange
-#define I_STEREOOFFSET          "csoffset"
+#define I_STEREOPARAMS          "cstereo"
 
 static const char s_shader_uniforms[] =
 	"\tfloat4 " I_POSNORMALMATRIX"[6];\n"
@@ -251,4 +251,4 @@ static const char s_shader_uniforms[] =
 	"\tfloat4 " I_NORMALMATRICES"[32];\n"
 	"\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n"
 	"\tfloat4 " I_DEPTHPARAMS";\n"
-	"\tfloat4 " I_STEREOOFFSET";\n";
+	"\tfloat4 " I_STEREOPARAMS";\n";
diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp
index f6d285cffd..7237ee41f1 100644
--- a/Source/Core/VideoCommon/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexShaderManager.cpp
@@ -515,13 +515,14 @@ void VertexShaderManager::SetConstants()
 
 		if (g_ActiveConfig.iStereoMode > 0 && xfmem.projection.type == GX_PERSPECTIVE)
 		{
-			float offset = g_ActiveConfig.iStereoSeparation / (200.0f * g_ActiveConfig.iStereoFocalLength);
-			constants.stereooffset[0] = (g_ActiveConfig.bStereoSwapEyes) ? offset : -offset;
-			constants.stereooffset[1] = (g_ActiveConfig.bStereoSwapEyes) ? -offset : offset;
+			float offset = g_ActiveConfig.iStereoSeparation / 10000.0f;
+			constants.stereoparams[0] = (g_ActiveConfig.bStereoSwapEyes) ? offset : -offset;
+			constants.stereoparams[1] = (g_ActiveConfig.bStereoSwapEyes) ? -offset : offset;
+			constants.stereoparams[2] = (float)g_ActiveConfig.iStereoFocalLength;
 		}
 		else
 		{
-			constants.stereooffset[0] = constants.stereooffset[1] = 0;
+			constants.stereoparams[0] = constants.stereoparams[1] = 0;
 		}
 
 		dirty = true;
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index 18f10b0fd2..d97a5a9377 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -67,8 +67,8 @@ void VideoConfig::Load(const std::string& ini_file)
 	settings->Get("FreeLook", &bFreeLook, 0);
 	settings->Get("UseFFV1", &bUseFFV1, 0);
 	settings->Get("StereoMode", &iStereoMode, 0);
-	settings->Get("StereoSeparation", &iStereoSeparation, 65);
-	settings->Get("StereoFocalLength", &iStereoFocalLength, 50);
+	settings->Get("StereoSeparation", &iStereoSeparation, 50);
+	settings->Get("StereoFocalLength", &iStereoFocalLength, 30);
 	settings->Get("StereoSwapEyes", &bStereoSwapEyes, false);
 	settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
 	settings->Get("FastDepthCalc", &bFastDepthCalc, true);

From 9994ccb3422b88649eb160bf373d4d29b08a4768 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sat, 8 Nov 2014 15:08:41 +0100
Subject: [PATCH 49/66] PostProcessing: Invalidate shader when anaglyph
 stereoscopy is toggled.

---
 Source/Core/VideoBackends/OGL/PostProcessing.cpp | 5 ++++-
 Source/Core/VideoBackends/OGL/PostProcessing.h   | 1 +
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.cpp b/Source/Core/VideoBackends/OGL/PostProcessing.cpp
index 84088459ed..80bdf8eef4 100644
--- a/Source/Core/VideoBackends/OGL/PostProcessing.cpp
+++ b/Source/Core/VideoBackends/OGL/PostProcessing.cpp
@@ -38,6 +38,7 @@ static char s_vertex_shader[] =
 
 OpenGLPostProcessing::OpenGLPostProcessing()
 	: m_initialized(false)
+	, m_anaglyph(false)
 {
 	CreateHeader();
 
@@ -160,7 +161,8 @@ void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle
 void OpenGLPostProcessing::ApplyShader()
 {
 	// shader didn't changed
-	if (m_initialized && m_config.GetShader() == g_ActiveConfig.sPostProcessingShader)
+	if (m_initialized && m_config.GetShader() == g_ActiveConfig.sPostProcessingShader &&
+			((g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH) == m_anaglyph))
 		return;
 
 	m_shader.Destroy();
@@ -222,6 +224,7 @@ void OpenGLPostProcessing::ApplyShader()
 		std::string glsl_name = "option_" + it.first;
 		m_uniform_bindings[it.first] = glGetUniformLocation(m_shader.glprogid, glsl_name.c_str());
 	}
+	m_anaglyph = g_ActiveConfig.iStereoMode == STEREO_ANAGLYPH;
 	m_initialized = true;
 }
 
diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.h b/Source/Core/VideoBackends/OGL/PostProcessing.h
index 4c67eca839..e2be0bdc7d 100644
--- a/Source/Core/VideoBackends/OGL/PostProcessing.h
+++ b/Source/Core/VideoBackends/OGL/PostProcessing.h
@@ -28,6 +28,7 @@ public:
 
 private:
 	bool m_initialized;
+	bool m_anaglyph;
 	SHADER m_shader;
 	GLuint m_uniform_resolution;
 	GLuint m_uniform_src_rect;

From 0f63186371c4f0bcc784fa8506d18b9514075d29 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sat, 8 Nov 2014 16:19:15 +0100
Subject: [PATCH 50/66] TextureCache: Add "Mono EFB Depth Copy" stereoscopy
 option.

---
 Source/Core/DolphinWX/VideoConfigDiag.cpp     |  3 ++
 .../Core/VideoBackends/OGL/TextureCache.cpp   | 28 ++++++++-----------
 Source/Core/VideoCommon/TextureCacheBase.cpp  |  4 ++-
 Source/Core/VideoCommon/TextureCacheBase.h    |  1 +
 Source/Core/VideoCommon/VideoConfig.cpp       |  3 ++
 Source/Core/VideoCommon/VideoConfig.h         |  1 +
 6 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index dae92a728a..3ec8010f1a 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -153,6 +153,7 @@ static wxString stereo_3d_desc = wxTRANSLATE("Select the stereoscopic 3D  mode,
 static wxString stereo_separation_desc = wxTRANSLATE("Control the interpupillary distance, this is the distance between the virtual eyes.");
 static wxString stereo_convergence_desc = wxTRANSLATE("Control the convergence distance, this controls the apparant distance of virtual objects.\nA lower value creates a stronger feeling of depth while a higher value is generally more comfortable.");
 static wxString stereo_swap_desc = wxTRANSLATE("Swap the left and right eye, mostly useful if you want to view side-by-side cross-eyed.\n\nIf unsure, leave this unchecked.");
+static wxString stereo_mono_depth_desc = wxTRANSLATE("Use the same depth buffer for both eyes in an EFB copy.\nThis is needed for games which use the depth buffer to calculate shadows.\n\nIf unsure, leave this unchecked.");
 
 
 #if !defined(__APPLE__)
@@ -473,6 +474,8 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 
 		szr_stereo->Add(CreateCheckBox(page_enh, _("Swap eyes"), wxGetTranslation(stereo_swap_desc), vconfig.bStereoSwapEyes));
 
+		szr_stereo->Add(CreateCheckBox(page_enh, _("Mono EFB Depth Copy"), wxGetTranslation(stereo_mono_depth_desc), vconfig.bStereoMonoEFBDepth));
+
 		wxStaticBoxSizer* const group_stereo = new wxStaticBoxSizer(wxVERTICAL, page_enh, _("Stereoscopy"));
 		group_stereo->Add(szr_stereo, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
 		szr_enh_main->Add(group_stereo, 0, wxEXPAND | wxALL, 5);
diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index a84da5dbb6..f635cb5fe6 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -361,7 +361,7 @@ void TextureCache::CompileShaders()
 		"out vec4 ocol0;\n"
 		"\n"
 		"void main(){\n"
-		"	vec4 texcol = texture(samp9, vec3(f_uv0.xy, 0));\n"
+		"	vec4 texcol = texture(samp9, vec3(f_uv0.xy, %s));\n"
 
 		// 255.99998474121 = 16777215/16777216*256
 		"	float workspace = texcol.x * 255.99998474121;\n"
@@ -387,30 +387,21 @@ void TextureCache::CompileShaders()
 		"	ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];\n"
 		"}\n";
 
-	const char *VProgram = (g_ActiveConfig.iStereoMode > 0) ?
-		"out vec2 v_uv0;\n"
+	const char *VProgram =
+		"out vec3 %s_uv0;\n"
 		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"uniform vec4 copy_position;\n" // left, top, right, bottom
 		"void main()\n"
 		"{\n"
 		"	vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
-		"	v_uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy);\n"
-		"	gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
-		"}\n" :
-		"out vec3 f_uv0;\n"
-		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
-		"uniform vec4 copy_position;\n" // left, top, right, bottom
-		"void main()\n"
-		"{\n"
-		"	vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
-		"	f_uv0 = vec3(mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy), 0.0);\n"
+		"	%s_uv0 = vec3(mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy), 0.0);\n"
 		"	gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
 		"}\n";
 
 	const char *GProgram = (g_ActiveConfig.iStereoMode > 0) ?
 		"layout(triangles) in;\n"
 		"layout(triangle_strip, max_vertices = 6) out;\n"
-		"in vec2 v_uv0[];\n"
+		"in vec3 v_uv0[];\n"
 		"out vec3 f_uv0;\n"
 		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"void main()\n"
@@ -418,7 +409,7 @@ void TextureCache::CompileShaders()
 		"	int layers = textureSize(samp9, 0).z;\n"
 		"	for (int layer = 0; layer < layers; ++layer) {\n"
 		"		for (int i = 0; i < gl_in.length(); ++i) {\n"
-		"			f_uv0 = vec3(v_uv0[i], layer);\n"
+		"			f_uv0 = vec3(v_uv0[i].xy, layer);\n"
 		"			gl_Position = gl_in[i].gl_Position;\n"
 		"			gl_Layer = layer;\n"
 		"			EmitVertex();\n"
@@ -427,8 +418,11 @@ void TextureCache::CompileShaders()
 		"	}\n"
 		"}\n" : nullptr;
 
-	ProgramShaderCache::CompileShader(s_ColorMatrixProgram, VProgram, pColorMatrixProg, GProgram);
-	ProgramShaderCache::CompileShader(s_DepthMatrixProgram, VProgram, pDepthMatrixProg, GProgram);
+	const char* prefix = (GProgram == nullptr) ? "f" : "v";
+	const char* depth_layer = (g_ActiveConfig.bStereoMonoEFBDepth) ? "0" : "f_uv0.z";
+
+	ProgramShaderCache::CompileShader(s_ColorMatrixProgram, StringFromFormat(VProgram, prefix, prefix).c_str(), pColorMatrixProg, GProgram);
+	ProgramShaderCache::CompileShader(s_DepthMatrixProgram, StringFromFormat(VProgram, prefix, prefix).c_str(), StringFromFormat(pDepthMatrixProg, depth_layer).c_str(), GProgram);
 
 	s_ColorMatrixUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "colmat");
 	s_DepthMatrixUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "colmat");
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index e057b379ad..7fa540022d 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -116,7 +116,8 @@ void TextureCache::OnConfigChanged(VideoConfig& config)
 			g_texture_cache->ClearRenderTargets();
 		}
 
-		if ((config.iStereoMode > 0) != backup_config.s_stereo_3d)
+		if ((config.iStereoMode > 0) != backup_config.s_stereo_3d ||
+			config.bStereoMonoEFBDepth != backup_config.s_mono_efb_depth)
 		{
 			g_texture_cache->DeleteShaders();
 			g_texture_cache->CompileShaders();
@@ -133,6 +134,7 @@ void TextureCache::OnConfigChanged(VideoConfig& config)
 	backup_config.s_hires_textures = config.bHiresTextures;
 	backup_config.s_copy_cache_enable = config.bEFBCopyCacheEnable;
 	backup_config.s_stereo_3d = config.iStereoMode > 0;
+	backup_config.s_mono_efb_depth = config.bStereoMonoEFBDepth;
 }
 
 void TextureCache::Cleanup()
diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h
index 64bc41f19d..d9cd2b8c16 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.h
+++ b/Source/Core/VideoCommon/TextureCacheBase.h
@@ -146,6 +146,7 @@ private:
 		bool s_hires_textures;
 		bool s_copy_cache_enable;
 		bool s_stereo_3d;
+		bool s_mono_efb_depth;
 	} backup_config;
 };
 
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index d97a5a9377..de2891fa74 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -70,6 +70,7 @@ void VideoConfig::Load(const std::string& ini_file)
 	settings->Get("StereoSeparation", &iStereoSeparation, 50);
 	settings->Get("StereoFocalLength", &iStereoFocalLength, 30);
 	settings->Get("StereoSwapEyes", &bStereoSwapEyes, false);
+	settings->Get("StereoMonoEFBDepth", &bStereoMonoEFBDepth, false);
 	settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
 	settings->Get("FastDepthCalc", &bFastDepthCalc, true);
 	settings->Get("MSAA", &iMultisampleMode, 0);
@@ -145,6 +146,7 @@ void VideoConfig::GameIniLoad()
 	CHECK_SETTING("Video_Settings", "StereoSeparation", iStereoSeparation);
 	CHECK_SETTING("Video_Settings", "StereoFocalLength", iStereoFocalLength);
 	CHECK_SETTING("Video_Settings", "StereoSwapEyes", bStereoSwapEyes);
+	CHECK_SETTING("Video_Settings", "StereoMonoEFBDepth", bStereoMonoEFBDepth);
 	CHECK_SETTING("Video_Settings", "EnablePixelLighting", bEnablePixelLighting);
 	CHECK_SETTING("Video_Settings", "FastDepthCalc", bFastDepthCalc);
 	CHECK_SETTING("Video_Settings", "MSAA", iMultisampleMode);
@@ -237,6 +239,7 @@ void VideoConfig::Save(const std::string& ini_file)
 	settings->Set("StereoSeparation", iStereoSeparation);
 	settings->Set("StereoFocalLength", iStereoFocalLength);
 	settings->Set("StereoSwapEyes", bStereoSwapEyes);
+	settings->Set("StereoMonoEFBDepth", bStereoMonoEFBDepth);
 	settings->Set("EnablePixelLighting", bEnablePixelLighting);
 	settings->Set("FastDepthCalc", bFastDepthCalc);
 	settings->Set("ShowEFBCopyRegions", bShowEFBCopyRegions);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 994264e58e..9d926a879b 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -83,6 +83,7 @@ struct VideoConfig final
 	int iStereoSeparation;
 	int iStereoFocalLength;
 	bool bStereoSwapEyes;
+	bool bStereoMonoEFBDepth;
 
 	// Information
 	bool bShowFPS;

From 21eabc1b9dbf9b8fbf382f958cfa84ee64b8ef2c Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sat, 8 Nov 2014 16:37:05 +0100
Subject: [PATCH 51/66] OGL: Add warning message when stereoscopic 3D is
 enabled but unsupported.

---
 Source/Core/VideoBackends/OGL/Render.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 4432a3e8d7..05557aa04d 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -582,6 +582,9 @@ Renderer::Renderer()
 				g_ogl_config.gl_renderer,
 				g_ogl_config.gl_version), 5000);
 
+	if (g_Config.iStereoMode > 0 && !g_Config.backend_info.bSupportsStereoscopy)
+		OSD::AddMessage("Stereoscopic 3D isn't supported by your GPU, support for OpenGL 3.2 is required.", 10000);
+
 	WARN_LOG(VIDEO,"Missing OGL Extensions: %s%s%s%s%s%s%s%s%s%s%s",
 			g_ActiveConfig.backend_info.bSupportsDualSourceBlend ? "" : "DualSourceBlend ",
 			g_ActiveConfig.backend_info.bSupportsPrimitiveRestart ? "" : "PrimitiveRestart ",

From 6e62dd34156e906638315b1a8c35ae3110e7dd38 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 11 Nov 2014 00:06:44 +0100
Subject: [PATCH 52/66] Add stereoscopy hotkeys.

---
 Source/Core/Core/ConfigManager.cpp  |  5 +++++
 Source/Core/Core/CoreParameter.h    |  5 +++++
 Source/Core/DolphinWX/Frame.cpp     | 20 ++++++++++++++++++++
 Source/Core/DolphinWX/HotkeyDlg.cpp |  5 +++++
 4 files changed, 35 insertions(+)

diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp
index 2c74b423b4..c1f0cf4288 100644
--- a/Source/Core/Core/ConfigManager.cpp
+++ b/Source/Core/Core/ConfigManager.cpp
@@ -81,6 +81,11 @@ static const struct
 	{ "FreelookZoomOut",      83 /* 'S' */,       4 /* wxMOD_SHIFT */ },
 	{ "FreelookReset",        82 /* 'R' */,       4 /* wxMOD_SHIFT */ },
 
+	{ "IncreaseSeparation",  0,                   0 /* wxMOD_NONE */ },
+	{ "DecreaseSeparation",  0,                   0 /* wxMOD_NONE */ },
+	{ "IncreaseConvergence", 0,                   0 /* wxMOD_NONE */ },
+	{ "DecreaseConvergence", 0,                   0 /* wxMOD_NONE */ },
+
 	{ "LoadStateSlot1",      340 /* WXK_F1 */,    0 /* wxMOD_NONE */ },
 	{ "LoadStateSlot2",      341 /* WXK_F2 */,    0 /* wxMOD_NONE */ },
 	{ "LoadStateSlot3",      342 /* WXK_F3 */,    0 /* wxMOD_NONE */ },
diff --git a/Source/Core/Core/CoreParameter.h b/Source/Core/Core/CoreParameter.h
index 28686a55bc..94087f77e7 100644
--- a/Source/Core/Core/CoreParameter.h
+++ b/Source/Core/Core/CoreParameter.h
@@ -54,6 +54,11 @@ enum Hotkey
 	HK_FREELOOK_ZOOM_OUT,
 	HK_FREELOOK_RESET,
 
+	HK_INCREASE_SEPARATION,
+	HK_DECREASE_SEPARATION,
+	HK_INCREASE_CONVERGENCE,
+	HK_DECREASE_CONVERGENCE,
+
 	HK_LOAD_STATE_SLOT_1,
 	HK_LOAD_STATE_SLOT_2,
 	HK_LOAD_STATE_SLOT_3,
diff --git a/Source/Core/DolphinWX/Frame.cpp b/Source/Core/DolphinWX/Frame.cpp
index 3784ae4bc9..ebaac0c66c 100644
--- a/Source/Core/DolphinWX/Frame.cpp
+++ b/Source/Core/DolphinWX/Frame.cpp
@@ -1123,6 +1123,26 @@ void CFrame::OnKeyDown(wxKeyEvent& event)
 		{
 			State::Load(g_saveSlot);
 		}
+		else if (IsHotkey(event, HK_INCREASE_SEPARATION))
+		{
+			if (++g_Config.iStereoSeparation > 100)
+				g_Config.iStereoSeparation = 100;
+		}
+		else if (IsHotkey(event, HK_DECREASE_SEPARATION))
+		{
+			if (--g_Config.iStereoSeparation < 10)
+				g_Config.iStereoSeparation = 10;
+		}
+		else if (IsHotkey(event, HK_INCREASE_CONVERGENCE))
+		{
+			if (++g_Config.iStereoFocalLength > 200)
+				g_Config.iStereoFocalLength = 200;
+		}
+		else if (IsHotkey(event, HK_DECREASE_CONVERGENCE))
+		{
+			if (--g_Config.iStereoFocalLength < 10)
+				g_Config.iStereoFocalLength = 10;
+		}
 
 		else
 		{
diff --git a/Source/Core/DolphinWX/HotkeyDlg.cpp b/Source/Core/DolphinWX/HotkeyDlg.cpp
index 2be49b904d..91fc6b90a2 100644
--- a/Source/Core/DolphinWX/HotkeyDlg.cpp
+++ b/Source/Core/DolphinWX/HotkeyDlg.cpp
@@ -238,6 +238,11 @@ void HotkeyConfigDialog::CreateHotkeyGUIControls()
 		_("Freelook Zoom Out"),
 		_("Freelook Reset"),
 
+		_("Increase Stereocopy Separation"),
+		_("Decrease Stereocopy Separation"),
+		_("Increase Stereocopy Convergence"),
+		_("Decrease Stereocopy Convergence"),
+
 		_("Load State Slot 1"),
 		_("Load State Slot 2"),
 		_("Load State Slot 3"),

From 24e4de364310880ecbe9cf396701fd9aede6dd6b Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 11 Nov 2014 00:53:03 +0100
Subject: [PATCH 53/66] VideoConfig: Move stereoscopy settings to the
 Enhancements section.

---
 Source/Core/VideoCommon/VideoConfig.cpp | 30 ++++++++++++-------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index de2891fa74..f30c13f388 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -66,11 +66,6 @@ void VideoConfig::Load(const std::string& ini_file)
 	settings->Get("DumpEFBTarget", &bDumpEFBTarget, 0);
 	settings->Get("FreeLook", &bFreeLook, 0);
 	settings->Get("UseFFV1", &bUseFFV1, 0);
-	settings->Get("StereoMode", &iStereoMode, 0);
-	settings->Get("StereoSeparation", &iStereoSeparation, 50);
-	settings->Get("StereoFocalLength", &iStereoFocalLength, 30);
-	settings->Get("StereoSwapEyes", &bStereoSwapEyes, false);
-	settings->Get("StereoMonoEFBDepth", &bStereoMonoEFBDepth, false);
 	settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0);
 	settings->Get("FastDepthCalc", &bFastDepthCalc, true);
 	settings->Get("MSAA", &iMultisampleMode, 0);
@@ -87,6 +82,11 @@ void VideoConfig::Load(const std::string& ini_file)
 	enhancements->Get("ForceFiltering", &bForceFiltering, 0);
 	enhancements->Get("MaxAnisotropy", &iMaxAnisotropy, 0);  // NOTE - this is x in (1 << x)
 	enhancements->Get("PostProcessingShader", &sPostProcessingShader, "");
+	enhancements->Get("StereoMode", &iStereoMode, 0);
+	enhancements->Get("StereoSeparation", &iStereoSeparation, 50);
+	enhancements->Get("StereoFocalLength", &iStereoFocalLength, 30);
+	enhancements->Get("StereoSwapEyes", &bStereoSwapEyes, false);
+	enhancements->Get("StereoMonoEFBDepth", &bStereoMonoEFBDepth, false);
 
 	IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks");
 	hacks->Get("EFBAccessEnable", &bEFBAccessEnable, true);
@@ -142,11 +142,6 @@ void VideoConfig::GameIniLoad()
 	CHECK_SETTING("Video_Settings", "UseRealXFB", bUseRealXFB);
 	CHECK_SETTING("Video_Settings", "SafeTextureCacheColorSamples", iSafeTextureCache_ColorSamples);
 	CHECK_SETTING("Video_Settings", "HiresTextures", bHiresTextures);
-	CHECK_SETTING("Video_Settings", "StereoMode", iStereoMode);
-	CHECK_SETTING("Video_Settings", "StereoSeparation", iStereoSeparation);
-	CHECK_SETTING("Video_Settings", "StereoFocalLength", iStereoFocalLength);
-	CHECK_SETTING("Video_Settings", "StereoSwapEyes", bStereoSwapEyes);
-	CHECK_SETTING("Video_Settings", "StereoMonoEFBDepth", bStereoMonoEFBDepth);
 	CHECK_SETTING("Video_Settings", "EnablePixelLighting", bEnablePixelLighting);
 	CHECK_SETTING("Video_Settings", "FastDepthCalc", bFastDepthCalc);
 	CHECK_SETTING("Video_Settings", "MSAA", iMultisampleMode);
@@ -183,6 +178,11 @@ void VideoConfig::GameIniLoad()
 	CHECK_SETTING("Video_Enhancements", "ForceFiltering", bForceFiltering);
 	CHECK_SETTING("Video_Enhancements", "MaxAnisotropy", iMaxAnisotropy);  // NOTE - this is x in (1 << x)
 	CHECK_SETTING("Video_Enhancements", "PostProcessingShader", sPostProcessingShader);
+	CHECK_SETTING("Video_Enhancements", "StereoMode", iStereoMode);
+	CHECK_SETTING("Video_Enhancements", "StereoSeparation", iStereoSeparation);
+	CHECK_SETTING("Video_Enhancements", "StereoFocalLength", iStereoFocalLength);
+	CHECK_SETTING("Video_Enhancements", "StereoSwapEyes", bStereoSwapEyes);
+	CHECK_SETTING("Video_Enhancements", "StereoMonoEFBDepth", bStereoMonoEFBDepth);
 
 	CHECK_SETTING("Video_Hacks", "EFBAccessEnable", bEFBAccessEnable);
 	CHECK_SETTING("Video_Hacks", "EFBCopyEnable", bEFBCopyEnable);
@@ -235,11 +235,6 @@ void VideoConfig::Save(const std::string& ini_file)
 	settings->Set("DumpEFBTarget", bDumpEFBTarget);
 	settings->Set("FreeLook", bFreeLook);
 	settings->Set("UseFFV1", bUseFFV1);
-	settings->Set("StereoMode", iStereoMode);
-	settings->Set("StereoSeparation", iStereoSeparation);
-	settings->Set("StereoFocalLength", iStereoFocalLength);
-	settings->Set("StereoSwapEyes", bStereoSwapEyes);
-	settings->Set("StereoMonoEFBDepth", bStereoMonoEFBDepth);
 	settings->Set("EnablePixelLighting", bEnablePixelLighting);
 	settings->Set("FastDepthCalc", bFastDepthCalc);
 	settings->Set("ShowEFBCopyRegions", bShowEFBCopyRegions);
@@ -257,6 +252,11 @@ void VideoConfig::Save(const std::string& ini_file)
 	enhancements->Set("ForceFiltering", bForceFiltering);
 	enhancements->Set("MaxAnisotropy", iMaxAnisotropy);
 	enhancements->Set("PostProcessingShader", sPostProcessingShader);
+	enhancements->Set("StereoMode", iStereoMode);
+	enhancements->Set("StereoSeparation", iStereoSeparation);
+	enhancements->Set("StereoFocalLength", iStereoFocalLength);
+	enhancements->Set("StereoSwapEyes", bStereoSwapEyes);
+	enhancements->Set("StereoMonoEFBDepth", bStereoMonoEFBDepth);
 
 	IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks");
 	hacks->Set("EFBAccessEnable", bEFBAccessEnable);

From 77078a8d756f8951a4bc6ee13be0cfa45d750356 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 11 Nov 2014 00:55:18 +0100
Subject: [PATCH 54/66] GameSettings: Force mono EFB depth copies in Luigi's
 Mansion.

This game uses stencil shadows which should not be stereoized.
---
 Data/Sys/GameSettings/GLME01.ini | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Data/Sys/GameSettings/GLME01.ini b/Data/Sys/GameSettings/GLME01.ini
index 5920388edd..832dd61df0 100644
--- a/Data/Sys/GameSettings/GLME01.ini
+++ b/Data/Sys/GameSettings/GLME01.ini
@@ -32,3 +32,5 @@ $99 of some treasures
 040AE530 3F000063
 $End Boss Has No HP
 
+[Video_Enhancements]
+StereoMonoEFBDepth = True

From 72c01e3083cd24474c4f1480443e60e2aecc2723 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 11 Nov 2014 00:58:16 +0100
Subject: [PATCH 55/66] VideoConfigDiag: Remove value labels.

---
 Source/Core/DolphinWX/VideoConfigDiag.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index 3ec8010f1a..b05de5374d 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -458,14 +458,14 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 		szr_stereo->Add(new wxStaticText(page_enh, -1, _("Stereoscopic 3D Mode:")), 1, wxALIGN_CENTER_VERTICAL, 0);
 		szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 4, stereo_choices));
 
-		wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 10, 100, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
+		wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 10, 100, wxDefaultPosition, wxDefaultSize);
 		sep_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoSep, this);
 		RegisterControl(sep_slider, wxGetTranslation(stereo_separation_desc));
 
 		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Separation:")), 1, wxALIGN_CENTER_VERTICAL, 0);
 		szr_stereo->Add(sep_slider, 0, wxEXPAND | wxRIGHT);
 
-		wxSlider* const conv_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoFocalLength, 10, 200, wxDefaultPosition, wxDefaultSize, wxSL_VALUE_LABEL);
+		wxSlider* const conv_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoFocalLength, 10, 200, wxDefaultPosition, wxDefaultSize);
 		conv_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoFoc, this);
 		RegisterControl(conv_slider, wxGetTranslation(stereo_convergence_desc));
 

From a565317a6acf06400cf5fa3b61473412fa4a600f Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Tue, 11 Nov 2014 01:03:15 +0100
Subject: [PATCH 56/66] VideoConfigDiag: Update convergence description.

The switch to the Nvidia formula reversed this setting.
---
 Source/Core/DolphinWX/VideoConfigDiag.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index b05de5374d..28ef75a173 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -151,7 +151,7 @@ static wxString cache_efb_copies_desc = wxTRANSLATE("Slightly speeds up EFB to R
 static wxString shader_errors_desc = wxTRANSLATE("Usually if shader compilation fails, an error message is displayed.\nHowever, one may skip the popups to allow interruption free gameplay by checking this option.\n\nIf unsure, leave this unchecked.");
 static wxString stereo_3d_desc = wxTRANSLATE("Select the stereoscopic 3D  mode, stereoscopy allows you to get a better feeling of depth if you have the necessary hardware.\nSide-by-Side and Top-and-Bottom are used by most 3D TVs.\nAnaglyph is used for Red-Cyan colored glasses.\nHeavily decreases emulation speed and sometimes causes issues.\n\nIf unsure, select Off.");
 static wxString stereo_separation_desc = wxTRANSLATE("Control the interpupillary distance, this is the distance between the virtual eyes.");
-static wxString stereo_convergence_desc = wxTRANSLATE("Control the convergence distance, this controls the apparant distance of virtual objects.\nA lower value creates a stronger feeling of depth while a higher value is generally more comfortable.");
+static wxString stereo_convergence_desc = wxTRANSLATE("Control the convergence distance, this controls the apparant distance of virtual objects.\nA higher value creates a stronger feeling of depth while a lower value is generally more comfortable.");
 static wxString stereo_swap_desc = wxTRANSLATE("Swap the left and right eye, mostly useful if you want to view side-by-side cross-eyed.\n\nIf unsure, leave this unchecked.");
 static wxString stereo_mono_depth_desc = wxTRANSLATE("Use the same depth buffer for both eyes in an EFB copy.\nThis is needed for games which use the depth buffer to calculate shadows.\n\nIf unsure, leave this unchecked.");
 

From 422125c1a94277e5e03ad57afe550c66eac1888d Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Wed, 12 Nov 2014 00:40:58 +0100
Subject: [PATCH 57/66] GeometryShaderGen: Add comments.

---
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index ff7632da05..c0093caffe 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -71,11 +71,14 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 
 	out.Write("void main()\n{\n");
 
+	// If the GPU supports invocation we don't need a for loop and can simply use the
+	// invocation identifier to determine which layer we're rendering.
 	if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
 		out.Write("\tlayer = gl_InvocationID;\n");
 	else
 		out.Write("\tfor (layer = 0; layer < %d; ++layer) {\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1);
 
+	// Select the output layer
 	out.Write("\tgl_Layer = layer;\n");
 
 	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
@@ -83,6 +86,13 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 
 	if (g_ActiveConfig.iStereoMode > 0)
 	{
+		// For stereoscopy add a small horizontal offset in Normalized Device Coordinates proportional
+		// to the depth of the vertex. We retrieve the depth value from the w-component of the projected
+		// vertex which contains the negated z-component of the original vertex.
+		// For negative parallax (out-of-screen effects) we subtract a convergence value from
+		// the depth value. This results in objects at a distance smaller than the convergence
+		// distance to seemingly appear in front of the screen.
+		// This formula is based on page 13 of the "Nvidia 3D Vision Automatic, Best Practices Guide"
 		out.Write("\t\tf.clipPos.x += " I_STEREOPARAMS"[layer] * (f.clipPos.w - " I_STEREOPARAMS"[2]);\n");
 		out.Write("\t\tf.pos.x += " I_STEREOPARAMS"[layer] * (f.pos.w - " I_STEREOPARAMS"[2]);\n");
 	}

From 106df04e8e87939014fb6ac8dcb35bb5f952176f Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sun, 16 Nov 2014 02:15:43 +0100
Subject: [PATCH 58/66] GeometryShaderGen: Declare the vertex array size.

---
 Source/Core/VideoBackends/OGL/TextureCache.cpp | 2 +-
 Source/Core/VideoCommon/GeometryShaderGen.cpp  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index f635cb5fe6..3fe9e2669e 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -401,7 +401,7 @@ void TextureCache::CompileShaders()
 	const char *GProgram = (g_ActiveConfig.iStereoMode > 0) ?
 		"layout(triangles) in;\n"
 		"layout(triangle_strip, max_vertices = 6) out;\n"
-		"in vec3 v_uv0[];\n"
+		"in vec3 v_uv0[3];\n"
 		"out vec3 f_uv0;\n"
 		"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
 		"void main()\n"
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index c0093caffe..b3ba0d7962 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -64,7 +64,7 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 
 	GenerateVSOutputStruct(out, ApiType);
 
-	out.Write("centroid in VS_OUTPUT o[];\n");
+	out.Write("centroid in VS_OUTPUT o[3];\n");
 	out.Write("centroid out VS_OUTPUT f;\n");
 
 	out.Write("flat out int layer;\n");

From ed9f258b275b61f4ee154e19412efee34f2f8759 Mon Sep 17 00:00:00 2001
From: degasus <wickmarkus@web.de>
Date: Sun, 16 Nov 2014 09:21:19 +0100
Subject: [PATCH 59/66] GeometryShader: Don't read from output variables

---
 .../Core/VideoBackends/OGL/TextureCache.cpp   |  2 +-
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 19 ++++++++++---------
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp
index 3fe9e2669e..66f633c6e3 100644
--- a/Source/Core/VideoBackends/OGL/TextureCache.cpp
+++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp
@@ -408,7 +408,7 @@ void TextureCache::CompileShaders()
 		"{\n"
 		"	int layers = textureSize(samp9, 0).z;\n"
 		"	for (int layer = 0; layer < layers; ++layer) {\n"
-		"		for (int i = 0; i < gl_in.length(); ++i) {\n"
+		"		for (int i = 0; i < 3; ++i) {\n"
 		"			f_uv0 = vec3(v_uv0[i].xy, layer);\n"
 		"			gl_Position = gl_in[i].gl_Position;\n"
 		"			gl_Layer = layer;\n"
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index b3ba0d7962..afaa16582b 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -74,15 +74,15 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	// If the GPU supports invocation we don't need a for loop and can simply use the
 	// invocation identifier to determine which layer we're rendering.
 	if (g_ActiveConfig.backend_info.bSupportsGSInstancing)
-		out.Write("\tlayer = gl_InvocationID;\n");
+		out.Write("\tint l = gl_InvocationID;\n");
 	else
-		out.Write("\tfor (layer = 0; layer < %d; ++layer) {\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1);
+		out.Write("\tfor (int l = 0; l < %d; ++l) {\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1);
 
-	// Select the output layer
-	out.Write("\tgl_Layer = layer;\n");
-
-	out.Write("\tfor (int i = 0; i < gl_in.length(); ++i) {\n");
+	out.Write("\tfor (int i = 0; i < 3; ++i) {\n");
+	out.Write("\t\tlayer = l;\n");
+	out.Write("\t\tgl_Layer = l;\n");
 	out.Write("\t\tf = o[i];\n");
+	out.Write("\t\tfloat4 pos = o[i].pos;\n");
 
 	if (g_ActiveConfig.iStereoMode > 0)
 	{
@@ -93,11 +93,12 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 		// the depth value. This results in objects at a distance smaller than the convergence
 		// distance to seemingly appear in front of the screen.
 		// This formula is based on page 13 of the "Nvidia 3D Vision Automatic, Best Practices Guide"
-		out.Write("\t\tf.clipPos.x += " I_STEREOPARAMS"[layer] * (f.clipPos.w - " I_STEREOPARAMS"[2]);\n");
-		out.Write("\t\tf.pos.x += " I_STEREOPARAMS"[layer] * (f.pos.w - " I_STEREOPARAMS"[2]);\n");
+		out.Write("\t\tf.clipPos.x = o[i].clipPos.x + " I_STEREOPARAMS"[l] * (o[i].clipPos.w - " I_STEREOPARAMS"[2]);\n");
+		out.Write("\t\tpos.x = o[i].pos.x + " I_STEREOPARAMS"[l] * (o[i].pos.w - " I_STEREOPARAMS"[2]);\n");
 	}
 
-	out.Write("\t\tgl_Position = f.pos;\n");
+	out.Write("\t\tf.pos.x = pos.x;\n");
+	out.Write("\t\tgl_Position = pos;\n");
 	out.Write("\t\tEmitVertex();\n");
 	out.Write("\t}\n");
 	out.Write("\tEndPrimitive();\n");

From 9b2cd82da5e81c74eea06ebc6cf49454eb4eaf4e Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Sat, 22 Nov 2014 19:18:45 +0100
Subject: [PATCH 60/66] GeometryShaderGen: Set the properties of the VS_OUTPUT
 struct in the uid.

---
 Source/Core/VideoCommon/GeometryShaderGen.cpp | 3 +++
 Source/Core/VideoCommon/GeometryShaderGen.h   | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp
index afaa16582b..83a590a7ed 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.cpp
+++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp
@@ -62,6 +62,9 @@ static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiTy
 	out.Write(s_shader_uniforms);
 	out.Write("};\n");
 
+	uid_data->numTexGens = xfmem.numTexGen.numTexGens;
+	uid_data->pixel_lighting = g_ActiveConfig.bEnablePixelLighting;
+
 	GenerateVSOutputStruct(out, ApiType);
 
 	out.Write("centroid in VS_OUTPUT o[3];\n");
diff --git a/Source/Core/VideoCommon/GeometryShaderGen.h b/Source/Core/VideoCommon/GeometryShaderGen.h
index fffa8b34ce..3f7dc5198b 100644
--- a/Source/Core/VideoCommon/GeometryShaderGen.h
+++ b/Source/Core/VideoCommon/GeometryShaderGen.h
@@ -14,6 +14,8 @@ struct geometry_shader_uid_data
 	u32 NumValues() const { return sizeof(geometry_shader_uid_data); }
 
 	u32 stereo : 1;
+	u32 numTexGens : 4;
+	u32 pixel_lighting : 1;
 };
 
 #pragma pack()

From 0f4d59f61207af19f4977c1a11afc8cac1acdd91 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Mon, 24 Nov 2014 11:50:35 +0100
Subject: [PATCH 61/66] VideoConfig: Rename StereoFocalLength to
 StereoConvergence.

---
 Source/Core/DolphinWX/Frame.cpp                 | 8 ++++----
 Source/Core/DolphinWX/VideoConfigDiag.cpp       | 2 +-
 Source/Core/DolphinWX/VideoConfigDiag.h         | 2 +-
 Source/Core/VideoCommon/VertexShaderManager.cpp | 2 +-
 Source/Core/VideoCommon/VideoConfig.cpp         | 6 +++---
 Source/Core/VideoCommon/VideoConfig.h           | 2 +-
 6 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/Source/Core/DolphinWX/Frame.cpp b/Source/Core/DolphinWX/Frame.cpp
index ebaac0c66c..9f1642c2f2 100644
--- a/Source/Core/DolphinWX/Frame.cpp
+++ b/Source/Core/DolphinWX/Frame.cpp
@@ -1135,13 +1135,13 @@ void CFrame::OnKeyDown(wxKeyEvent& event)
 		}
 		else if (IsHotkey(event, HK_INCREASE_CONVERGENCE))
 		{
-			if (++g_Config.iStereoFocalLength > 200)
-				g_Config.iStereoFocalLength = 200;
+			if (++g_Config.iStereoConvergence > 200)
+				g_Config.iStereoConvergence = 200;
 		}
 		else if (IsHotkey(event, HK_DECREASE_CONVERGENCE))
 		{
-			if (--g_Config.iStereoFocalLength < 10)
-				g_Config.iStereoFocalLength = 10;
+			if (--g_Config.iStereoConvergence < 10)
+				g_Config.iStereoConvergence = 10;
 		}
 
 		else
diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index 28ef75a173..b930ec2dbe 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -465,7 +465,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Separation:")), 1, wxALIGN_CENTER_VERTICAL, 0);
 		szr_stereo->Add(sep_slider, 0, wxEXPAND | wxRIGHT);
 
-		wxSlider* const conv_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoFocalLength, 10, 200, wxDefaultPosition, wxDefaultSize);
+		wxSlider* const conv_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoConvergence, 10, 200, wxDefaultPosition, wxDefaultSize);
 		conv_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoFoc, this);
 		RegisterControl(conv_slider, wxGetTranslation(stereo_convergence_desc));
 
diff --git a/Source/Core/DolphinWX/VideoConfigDiag.h b/Source/Core/DolphinWX/VideoConfigDiag.h
index 7900039bea..10dc808bb9 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.h
+++ b/Source/Core/DolphinWX/VideoConfigDiag.h
@@ -174,7 +174,7 @@ protected:
 
 	void Event_StereoFoc(wxCommandEvent &ev)
 	{
-		vconfig.iStereoFocalLength = ev.GetInt();
+		vconfig.iStereoConvergence = ev.GetInt();
 
 		ev.Skip();
 	}
diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp
index 7237ee41f1..f71dd6514b 100644
--- a/Source/Core/VideoCommon/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexShaderManager.cpp
@@ -518,7 +518,7 @@ void VertexShaderManager::SetConstants()
 			float offset = g_ActiveConfig.iStereoSeparation / 10000.0f;
 			constants.stereoparams[0] = (g_ActiveConfig.bStereoSwapEyes) ? offset : -offset;
 			constants.stereoparams[1] = (g_ActiveConfig.bStereoSwapEyes) ? -offset : offset;
-			constants.stereoparams[2] = (float)g_ActiveConfig.iStereoFocalLength;
+			constants.stereoparams[2] = (float)g_ActiveConfig.iStereoConvergence;
 		}
 		else
 		{
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index f30c13f388..2caeaee778 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -84,7 +84,7 @@ void VideoConfig::Load(const std::string& ini_file)
 	enhancements->Get("PostProcessingShader", &sPostProcessingShader, "");
 	enhancements->Get("StereoMode", &iStereoMode, 0);
 	enhancements->Get("StereoSeparation", &iStereoSeparation, 50);
-	enhancements->Get("StereoFocalLength", &iStereoFocalLength, 30);
+	enhancements->Get("StereoConvergence", &iStereoConvergence, 30);
 	enhancements->Get("StereoSwapEyes", &bStereoSwapEyes, false);
 	enhancements->Get("StereoMonoEFBDepth", &bStereoMonoEFBDepth, false);
 
@@ -180,7 +180,7 @@ void VideoConfig::GameIniLoad()
 	CHECK_SETTING("Video_Enhancements", "PostProcessingShader", sPostProcessingShader);
 	CHECK_SETTING("Video_Enhancements", "StereoMode", iStereoMode);
 	CHECK_SETTING("Video_Enhancements", "StereoSeparation", iStereoSeparation);
-	CHECK_SETTING("Video_Enhancements", "StereoFocalLength", iStereoFocalLength);
+	CHECK_SETTING("Video_Enhancements", "StereoConvergence", iStereoConvergence);
 	CHECK_SETTING("Video_Enhancements", "StereoSwapEyes", bStereoSwapEyes);
 	CHECK_SETTING("Video_Enhancements", "StereoMonoEFBDepth", bStereoMonoEFBDepth);
 
@@ -254,7 +254,7 @@ void VideoConfig::Save(const std::string& ini_file)
 	enhancements->Set("PostProcessingShader", sPostProcessingShader);
 	enhancements->Set("StereoMode", iStereoMode);
 	enhancements->Set("StereoSeparation", iStereoSeparation);
-	enhancements->Set("StereoFocalLength", iStereoFocalLength);
+	enhancements->Set("StereoConvergence", iStereoConvergence);
 	enhancements->Set("StereoSwapEyes", bStereoSwapEyes);
 	enhancements->Set("StereoMonoEFBDepth", bStereoMonoEFBDepth);
 
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 9d926a879b..c64ea8377d 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -81,7 +81,7 @@ struct VideoConfig final
 	std::string sPostProcessingShader;
 	int iStereoMode;
 	int iStereoSeparation;
-	int iStereoFocalLength;
+	int iStereoConvergence;
 	bool bStereoSwapEyes;
 	bool bStereoMonoEFBDepth;
 

From f64aadd3621941c9910d21a595175e59ae1d30e9 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Mon, 24 Nov 2014 12:01:21 +0100
Subject: [PATCH 62/66] VideoConfig: Limit the StereoMonoEFBDepth option to the
 game ini and introduce a separate section for it.

---
 Data/Sys/GameSettings/GLME01.ini          | 2 +-
 Source/Core/DolphinWX/VideoConfigDiag.cpp | 5 +----
 Source/Core/VideoCommon/VideoConfig.cpp   | 8 +++++---
 Source/Core/VideoCommon/VideoConfig.h     | 4 +++-
 4 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/Data/Sys/GameSettings/GLME01.ini b/Data/Sys/GameSettings/GLME01.ini
index 832dd61df0..ccabdf2a10 100644
--- a/Data/Sys/GameSettings/GLME01.ini
+++ b/Data/Sys/GameSettings/GLME01.ini
@@ -32,5 +32,5 @@ $99 of some treasures
 040AE530 3F000063
 $End Boss Has No HP
 
-[Video_Enhancements]
+[Video_Stereoscopy]
 StereoMonoEFBDepth = True
diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index b930ec2dbe..a2b1d4812a 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -153,7 +153,6 @@ static wxString stereo_3d_desc = wxTRANSLATE("Select the stereoscopic 3D  mode,
 static wxString stereo_separation_desc = wxTRANSLATE("Control the interpupillary distance, this is the distance between the virtual eyes.");
 static wxString stereo_convergence_desc = wxTRANSLATE("Control the convergence distance, this controls the apparant distance of virtual objects.\nA higher value creates a stronger feeling of depth while a lower value is generally more comfortable.");
 static wxString stereo_swap_desc = wxTRANSLATE("Swap the left and right eye, mostly useful if you want to view side-by-side cross-eyed.\n\nIf unsure, leave this unchecked.");
-static wxString stereo_mono_depth_desc = wxTRANSLATE("Use the same depth buffer for both eyes in an EFB copy.\nThis is needed for games which use the depth buffer to calculate shadows.\n\nIf unsure, leave this unchecked.");
 
 
 #if !defined(__APPLE__)
@@ -472,9 +471,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Convergence:")), 1, wxALIGN_CENTER_VERTICAL, 0);
 		szr_stereo->Add(conv_slider, 0, wxEXPAND | wxRIGHT);
 
-		szr_stereo->Add(CreateCheckBox(page_enh, _("Swap eyes"), wxGetTranslation(stereo_swap_desc), vconfig.bStereoSwapEyes));
-
-		szr_stereo->Add(CreateCheckBox(page_enh, _("Mono EFB Depth Copy"), wxGetTranslation(stereo_mono_depth_desc), vconfig.bStereoMonoEFBDepth));
+		szr_stereo->Add(CreateCheckBox(page_enh, _("Swap Eyes"), wxGetTranslation(stereo_swap_desc), vconfig.bStereoSwapEyes));
 
 		wxStaticBoxSizer* const group_stereo = new wxStaticBoxSizer(wxVERTICAL, page_enh, _("Stereoscopy"));
 		group_stereo->Add(szr_stereo, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index 2caeaee778..76baa6d1d0 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -38,6 +38,9 @@ VideoConfig::VideoConfig()
 	backend_info.APIType = API_NONE;
 	backend_info.bUseMinimalMipCount = false;
 	backend_info.bSupportsExclusiveFullscreen = false;
+
+	// Game-specific stereoscopy settings
+	bStereoMonoEFBDepth = false;
 }
 
 void VideoConfig::Load(const std::string& ini_file)
@@ -86,7 +89,6 @@ void VideoConfig::Load(const std::string& ini_file)
 	enhancements->Get("StereoSeparation", &iStereoSeparation, 50);
 	enhancements->Get("StereoConvergence", &iStereoConvergence, 30);
 	enhancements->Get("StereoSwapEyes", &bStereoSwapEyes, false);
-	enhancements->Get("StereoMonoEFBDepth", &bStereoMonoEFBDepth, false);
 
 	IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks");
 	hacks->Get("EFBAccessEnable", &bEFBAccessEnable, true);
@@ -182,7 +184,8 @@ void VideoConfig::GameIniLoad()
 	CHECK_SETTING("Video_Enhancements", "StereoSeparation", iStereoSeparation);
 	CHECK_SETTING("Video_Enhancements", "StereoConvergence", iStereoConvergence);
 	CHECK_SETTING("Video_Enhancements", "StereoSwapEyes", bStereoSwapEyes);
-	CHECK_SETTING("Video_Enhancements", "StereoMonoEFBDepth", bStereoMonoEFBDepth);
+
+	CHECK_SETTING("Video_Stereoscopy", "StereoMonoEFBDepth", bStereoMonoEFBDepth);
 
 	CHECK_SETTING("Video_Hacks", "EFBAccessEnable", bEFBAccessEnable);
 	CHECK_SETTING("Video_Hacks", "EFBCopyEnable", bEFBCopyEnable);
@@ -256,7 +259,6 @@ void VideoConfig::Save(const std::string& ini_file)
 	enhancements->Set("StereoSeparation", iStereoSeparation);
 	enhancements->Set("StereoConvergence", iStereoConvergence);
 	enhancements->Set("StereoSwapEyes", bStereoSwapEyes);
-	enhancements->Set("StereoMonoEFBDepth", bStereoMonoEFBDepth);
 
 	IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks");
 	hacks->Set("EFBAccessEnable", bEFBAccessEnable);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index c64ea8377d..67bb9e4439 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -83,7 +83,6 @@ struct VideoConfig final
 	int iStereoSeparation;
 	int iStereoConvergence;
 	bool bStereoSwapEyes;
-	bool bStereoMonoEFBDepth;
 
 	// Information
 	bool bShowFPS;
@@ -125,6 +124,9 @@ struct VideoConfig final
 	int iLog; // CONF_ bits
 	int iSaveTargetId; // TODO: Should be dropped
 
+	// Stereoscopy
+	bool bStereoMonoEFBDepth;
+
 	// D3D only config, mostly to be merged into the above
 	int iAdapter;
 

From a04edbcb9a5ffe9351c75446982e3832b6d3e5a4 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Mon, 24 Nov 2014 12:19:25 +0100
Subject: [PATCH 63/66] VideoConfig: Add separation and convergence modifiers
 to the game ini.

---
 Source/Core/VideoCommon/VertexShaderManager.cpp | 4 ++--
 Source/Core/VideoCommon/VideoConfig.cpp         | 4 ++++
 Source/Core/VideoCommon/VideoConfig.h           | 2 ++
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp
index f71dd6514b..8c9791f276 100644
--- a/Source/Core/VideoCommon/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexShaderManager.cpp
@@ -515,10 +515,10 @@ void VertexShaderManager::SetConstants()
 
 		if (g_ActiveConfig.iStereoMode > 0 && xfmem.projection.type == GX_PERSPECTIVE)
 		{
-			float offset = g_ActiveConfig.iStereoSeparation / 10000.0f;
+			float offset = (g_ActiveConfig.iStereoSeparation / 10000.0f) * (g_ActiveConfig.iStereoSeparationPercent / 100.0f);
 			constants.stereoparams[0] = (g_ActiveConfig.bStereoSwapEyes) ? offset : -offset;
 			constants.stereoparams[1] = (g_ActiveConfig.bStereoSwapEyes) ? -offset : offset;
-			constants.stereoparams[2] = (float)g_ActiveConfig.iStereoConvergence;
+			constants.stereoparams[2] = g_ActiveConfig.iStereoConvergence * (g_ActiveConfig.iStereoConvergencePercent / 100.0f);
 		}
 		else
 		{
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index 76baa6d1d0..741d671a34 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -41,6 +41,8 @@ VideoConfig::VideoConfig()
 
 	// Game-specific stereoscopy settings
 	bStereoMonoEFBDepth = false;
+	iStereoSeparationPercent = 100;
+	iStereoConvergencePercent = 100;
 }
 
 void VideoConfig::Load(const std::string& ini_file)
@@ -186,6 +188,8 @@ void VideoConfig::GameIniLoad()
 	CHECK_SETTING("Video_Enhancements", "StereoSwapEyes", bStereoSwapEyes);
 
 	CHECK_SETTING("Video_Stereoscopy", "StereoMonoEFBDepth", bStereoMonoEFBDepth);
+	CHECK_SETTING("Video_Stereoscopy", "StereoSeparationPercent", iStereoSeparationPercent);
+	CHECK_SETTING("Video_Stereoscopy", "StereoConvergencePercent", iStereoConvergencePercent);
 
 	CHECK_SETTING("Video_Hacks", "EFBAccessEnable", bEFBAccessEnable);
 	CHECK_SETTING("Video_Hacks", "EFBCopyEnable", bEFBCopyEnable);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 67bb9e4439..c825f8ec53 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -126,6 +126,8 @@ struct VideoConfig final
 
 	// Stereoscopy
 	bool bStereoMonoEFBDepth;
+	int iStereoSeparationPercent;
+	int iStereoConvergencePercent;
 
 	// D3D only config, mostly to be merged into the above
 	int iAdapter;

From 145e0cc84c9e3a03b50d5455b5ac8fc79c854760 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Mon, 24 Nov 2014 13:19:34 +0100
Subject: [PATCH 64/66] OGL: Display the stereoscopy support warning before the
 stereo setting is reset.

Previously the message would never display, because stereoscopy would be turned off before the warning.
---
 Source/Core/VideoBackends/OGL/Render.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index 05557aa04d..b44004edc2 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -563,6 +563,9 @@ Renderer::Renderer()
 		bSuccess = false;
 	}
 
+	if (g_Config.iStereoMode > 0 && !g_Config.backend_info.bSupportsStereoscopy)
+		OSD::AddMessage("Stereoscopic 3D isn't supported by your GPU, support for OpenGL 3.2 is required.", 10000);
+
 	if (!bSuccess)
 	{
 		// Not all needed extensions are supported, so we have to stop here.
@@ -582,9 +585,6 @@ Renderer::Renderer()
 				g_ogl_config.gl_renderer,
 				g_ogl_config.gl_version), 5000);
 
-	if (g_Config.iStereoMode > 0 && !g_Config.backend_info.bSupportsStereoscopy)
-		OSD::AddMessage("Stereoscopic 3D isn't supported by your GPU, support for OpenGL 3.2 is required.", 10000);
-
 	WARN_LOG(VIDEO,"Missing OGL Extensions: %s%s%s%s%s%s%s%s%s%s%s",
 			g_ActiveConfig.backend_info.bSupportsDualSourceBlend ? "" : "DualSourceBlend ",
 			g_ActiveConfig.backend_info.bSupportsPrimitiveRestart ? "" : "PrimitiveRestart ",

From b3c6602d06b78d13201952e0cc5fb3a4c8171de1 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 27 Nov 2014 15:21:06 +0100
Subject: [PATCH 65/66] OGL: Only show the options when stereoscopy was enabled
 in the INI file.

We'll enable the stereoscopy options globally when it is ready for release.
---
 Source/Core/DolphinWX/VideoConfigDiag.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index a2b1d4812a..c6bb94e996 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -449,7 +449,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 
 	// - stereoscopy
 
-	if (vconfig.backend_info.bSupportsStereoscopy)
+	if (vconfig.backend_info.bSupportsStereoscopy && vconfig.iStereoMode > 0)
 	{
 		wxFlexGridSizer* const szr_stereo = new wxFlexGridSizer(2, 5, 5);
 

From 6d51455195e95a1bb97eb263fcd3423c757f59e7 Mon Sep 17 00:00:00 2001
From: Jules Blok <jules.blok@gmail.com>
Date: Thu, 27 Nov 2014 15:28:00 +0100
Subject: [PATCH 66/66] VideoConfig: Further optimize convergence and
 separation values and update their descriptions.

Previous convergence distance was much too large.
---
 Source/Core/DolphinWX/Frame.cpp                 | 12 ++++++------
 Source/Core/DolphinWX/VideoConfigDiag.cpp       |  8 ++++----
 Source/Core/VideoCommon/VertexShaderManager.cpp |  4 ++--
 Source/Core/VideoCommon/VideoConfig.cpp         |  4 ++--
 4 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/Source/Core/DolphinWX/Frame.cpp b/Source/Core/DolphinWX/Frame.cpp
index 9f1642c2f2..d4b7ba65f7 100644
--- a/Source/Core/DolphinWX/Frame.cpp
+++ b/Source/Core/DolphinWX/Frame.cpp
@@ -1130,18 +1130,18 @@ void CFrame::OnKeyDown(wxKeyEvent& event)
 		}
 		else if (IsHotkey(event, HK_DECREASE_SEPARATION))
 		{
-			if (--g_Config.iStereoSeparation < 10)
-				g_Config.iStereoSeparation = 10;
+			if (--g_Config.iStereoSeparation < 0)
+				g_Config.iStereoSeparation = 0;
 		}
 		else if (IsHotkey(event, HK_INCREASE_CONVERGENCE))
 		{
-			if (++g_Config.iStereoConvergence > 200)
-				g_Config.iStereoConvergence = 200;
+			if (++g_Config.iStereoConvergence > 500)
+				g_Config.iStereoConvergence = 500;
 		}
 		else if (IsHotkey(event, HK_DECREASE_CONVERGENCE))
 		{
-			if (--g_Config.iStereoConvergence < 10)
-				g_Config.iStereoConvergence = 10;
+			if (--g_Config.iStereoConvergence < 0)
+				g_Config.iStereoConvergence = 0;
 		}
 
 		else
diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp
index c6bb94e996..ab01c4339a 100644
--- a/Source/Core/DolphinWX/VideoConfigDiag.cpp
+++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp
@@ -150,8 +150,8 @@ static wxString ppshader_desc = wxTRANSLATE("Apply a post-processing effect afte
 static wxString cache_efb_copies_desc = wxTRANSLATE("Slightly speeds up EFB to RAM copies by sacrificing emulation accuracy.\nSometimes also increases visual quality.\nIf you're experiencing any issues, try raising texture cache accuracy or disable this option.\n\nIf unsure, leave this unchecked.");
 static wxString shader_errors_desc = wxTRANSLATE("Usually if shader compilation fails, an error message is displayed.\nHowever, one may skip the popups to allow interruption free gameplay by checking this option.\n\nIf unsure, leave this unchecked.");
 static wxString stereo_3d_desc = wxTRANSLATE("Select the stereoscopic 3D  mode, stereoscopy allows you to get a better feeling of depth if you have the necessary hardware.\nSide-by-Side and Top-and-Bottom are used by most 3D TVs.\nAnaglyph is used for Red-Cyan colored glasses.\nHeavily decreases emulation speed and sometimes causes issues.\n\nIf unsure, select Off.");
-static wxString stereo_separation_desc = wxTRANSLATE("Control the interpupillary distance, this is the distance between the virtual eyes.");
-static wxString stereo_convergence_desc = wxTRANSLATE("Control the convergence distance, this controls the apparant distance of virtual objects.\nA higher value creates a stronger feeling of depth while a lower value is generally more comfortable.");
+static wxString stereo_separation_desc = wxTRANSLATE("Control the separation distance, this is the distance between the virtual cameras.\nA higher value creates a stronger feeling of depth while a lower value is more comfortable.");
+static wxString stereo_convergence_desc = wxTRANSLATE("Control the convergence distance, this controls the apparant distance of virtual objects.\nA higher value creates stronger out-of-screen effects while a lower value is more comfortable.");
 static wxString stereo_swap_desc = wxTRANSLATE("Swap the left and right eye, mostly useful if you want to view side-by-side cross-eyed.\n\nIf unsure, leave this unchecked.");
 
 
@@ -457,14 +457,14 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
 		szr_stereo->Add(new wxStaticText(page_enh, -1, _("Stereoscopic 3D Mode:")), 1, wxALIGN_CENTER_VERTICAL, 0);
 		szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 4, stereo_choices));
 
-		wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 10, 100, wxDefaultPosition, wxDefaultSize);
+		wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoSeparation, 0, 100, wxDefaultPosition, wxDefaultSize);
 		sep_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoSep, this);
 		RegisterControl(sep_slider, wxGetTranslation(stereo_separation_desc));
 
 		szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Separation:")), 1, wxALIGN_CENTER_VERTICAL, 0);
 		szr_stereo->Add(sep_slider, 0, wxEXPAND | wxRIGHT);
 
-		wxSlider* const conv_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoConvergence, 10, 200, wxDefaultPosition, wxDefaultSize);
+		wxSlider* const conv_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoConvergence, 0, 500, wxDefaultPosition, wxDefaultSize);
 		conv_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoFoc, this);
 		RegisterControl(conv_slider, wxGetTranslation(stereo_convergence_desc));
 
diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp
index 8c9791f276..8de50c5436 100644
--- a/Source/Core/VideoCommon/VertexShaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexShaderManager.cpp
@@ -515,10 +515,10 @@ void VertexShaderManager::SetConstants()
 
 		if (g_ActiveConfig.iStereoMode > 0 && xfmem.projection.type == GX_PERSPECTIVE)
 		{
-			float offset = (g_ActiveConfig.iStereoSeparation / 10000.0f) * (g_ActiveConfig.iStereoSeparationPercent / 100.0f);
+			float offset = (g_ActiveConfig.iStereoSeparation / 1000.0f) * (g_ActiveConfig.iStereoSeparationPercent / 100.0f);
 			constants.stereoparams[0] = (g_ActiveConfig.bStereoSwapEyes) ? offset : -offset;
 			constants.stereoparams[1] = (g_ActiveConfig.bStereoSwapEyes) ? -offset : offset;
-			constants.stereoparams[2] = g_ActiveConfig.iStereoConvergence * (g_ActiveConfig.iStereoConvergencePercent / 100.0f);
+			constants.stereoparams[2] = (g_ActiveConfig.iStereoConvergence / 10.0f) * (g_ActiveConfig.iStereoConvergencePercent / 100.0f);
 		}
 		else
 		{
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index 741d671a34..2b6025f13e 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -88,8 +88,8 @@ void VideoConfig::Load(const std::string& ini_file)
 	enhancements->Get("MaxAnisotropy", &iMaxAnisotropy, 0);  // NOTE - this is x in (1 << x)
 	enhancements->Get("PostProcessingShader", &sPostProcessingShader, "");
 	enhancements->Get("StereoMode", &iStereoMode, 0);
-	enhancements->Get("StereoSeparation", &iStereoSeparation, 50);
-	enhancements->Get("StereoConvergence", &iStereoConvergence, 30);
+	enhancements->Get("StereoSeparation", &iStereoSeparation, 20);
+	enhancements->Get("StereoConvergence", &iStereoConvergence, 20);
 	enhancements->Get("StereoSwapEyes", &bStereoSwapEyes, false);
 
 	IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks");