Add texture filtering precision hint

A new extension will be added to SwiftShader in order to allow
Chromium to trigger high precision filtering when necessary.
This extension is documented in:
extensions/CHROMIUM_texture_filtering_hint.txt

Bug swiftshader:76

Change-Id: I7c5b5c5fd01afbd7079e7949ecbd9c18fc539f2b
Reviewed-on: https://swiftshader-review.googlesource.com/10708
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/extensions/CHROMIUM_texture_filtering_hint.txt b/extensions/CHROMIUM_texture_filtering_hint.txt
new file mode 100644
index 0000000..38af6e0
--- /dev/null
+++ b/extensions/CHROMIUM_texture_filtering_hint.txt
@@ -0,0 +1,85 @@
+Name
+
+    CHROMIUM_texture_filtering_hint
+
+Name Strings
+
+    GL_CHROMIUM_texture_filtering_hint
+
+Contributors
+
+    Alexis Hetu, Google Inc.
+    Nicolas Capens, Google Inc.
+    Shannon Woods, Google Inc.
+
+Contact
+
+    Alexis Hetu, Google Inc. (sugoi 'at' chromium 'dot' org)
+
+Version
+
+    Last Modifed Date: July 18, 2017
+
+Dependencies
+
+    This extension is written against the OpenGL ES 2.0 specification.
+
+    OpenGL ES 2.0 is required.
+
+Overview
+
+    This extension defines a way to request high precision texture filtering
+    using a new value to Hint.
+
+    When this extension is enabled, TEXTURE_FILTERING_HINT_CHROMIUM can be used
+    by the implementation as a means to distinguish between a performance
+    focused implementation, using FASTEST, or a precision focused
+    implementation, using NICEST.
+
+    Like other hints, either option is spec compliant and the behavior of
+    DONT_CARE is implementation specific.
+
+New Tokens
+
+    Accepted by the <pname> parameter of GetIntegerv, GetFloatv and GetBooleanv
+    and by the <target> parameter of Hint:
+
+    TEXTURE_FILTERING_HINT_CHROMIUM      0x8AF0
+
+New Procedures and Functions
+
+    None.
+
+Errors
+
+    None.
+
+New State
+
+    None.
+
+Issues
+
+    1) When does the hint take effect?
+
+       At the time of the next draw call, and all subsequent draw calls.
+  
+    2) Does the first draw call after the filtering hint is changed use the
+       updated filtering method?
+
+       Yes
+ 
+    3) Can I switch it back and forth between every draw call, multiple times
+       during a single frame?
+
+       Yes
+ 
+    4) Do program objects which were created before the filtering hint was
+       changed and which contain sampling instructions use the filtering method
+       from when they were created, or the method at the time of draw call?
+
+       At the time of draw call.
+
+Revision History
+
+    2/7/2014    Documented the extension
diff --git a/src/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp
index 2265aa6..c139068 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -100,6 +100,7 @@
 	mState.rasterizerDiscardEnabled = false;
 	mState.generateMipmapHint = GL_DONT_CARE;
 	mState.fragmentShaderDerivativeHint = GL_DONT_CARE;
+	mState.textureFilteringHint = GL_DONT_CARE;
 
 	mState.lineWidth = 1.0f;
 
@@ -682,6 +683,11 @@
 	// Ignore for now. It is valid for implementations to ignore hint.
 }
 
+void Context::setTextureFilteringHint(GLenum hint)
+{
+	mState.textureFilteringHint = hint;
+}
+
 void Context::setViewportParams(GLint x, GLint y, GLsizei width, GLsizei height)
 {
 	mState.viewportX = x;
@@ -1890,6 +1896,7 @@
 	case GL_UNPACK_ALIGNMENT:                 *params = mState.unpackInfo.alignment;          return true;
 	case GL_GENERATE_MIPMAP_HINT:             *params = mState.generateMipmapHint;            return true;
 	case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES: *params = mState.fragmentShaderDerivativeHint; return true;
+	case GL_TEXTURE_FILTERING_HINT_CHROMIUM:  *params = mState.textureFilteringHint;          return true;
 	case GL_ACTIVE_TEXTURE:                   *params = (mState.activeSampler + GL_TEXTURE0); return true;
 	case GL_STENCIL_FUNC:                     *params = mState.stencilFunc;                   return true;
 	case GL_STENCIL_REF:                      *params = mState.stencilRef;                    return true;
@@ -2425,6 +2432,7 @@
 	case GL_UNPACK_ALIGNMENT:
 	case GL_GENERATE_MIPMAP_HINT:
 	case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES:
+	case GL_TEXTURE_FILTERING_HINT_CHROMIUM:
 	case GL_RED_BITS:
 	case GL_GREEN_BITS:
 	case GL_BLUE_BITS:
@@ -3058,6 +3066,7 @@
 				device->setTextureFilter(samplerType, samplerIndex, es2sw::ConvertTextureFilter(minFilter, magFilter, maxAnisotropy));
 				device->setMipmapFilter(samplerType, samplerIndex, es2sw::ConvertMipMapFilter(minFilter));
 				device->setMaxAnisotropy(samplerType, samplerIndex, maxAnisotropy);
+				device->setHighPrecisionFiltering(samplerType, samplerIndex, mState.textureFilteringHint == GL_NICEST);
 
 				applyTexture(samplerType, samplerIndex, texture);
 			}
@@ -4324,6 +4333,7 @@
 		"GL_ANGLE_texture_compression_dxt3",
 		"GL_ANGLE_texture_compression_dxt5",
 #endif
+		"GL_CHROMIUM_texture_filtering_hint",
 		"GL_NV_fence",
 		"GL_NV_framebuffer_blit",
 		"GL_NV_read_depth",
diff --git a/src/OpenGL/libGLESv2/Context.h b/src/OpenGL/libGLESv2/Context.h
index bbe6ddd..16e0aa2 100644
--- a/src/OpenGL/libGLESv2/Context.h
+++ b/src/OpenGL/libGLESv2/Context.h
@@ -156,6 +156,8 @@
 #endif
 };
 
+const GLenum GL_TEXTURE_FILTERING_HINT_CHROMIUM = 0x8AF0;
+
 const GLint NUM_COMPRESSED_TEXTURE_FORMATS = sizeof(compressedTextureFormats) / sizeof(compressedTextureFormats[0]);
 
 const GLint multisampleCount[] = {4, 2, 1};
@@ -376,6 +378,7 @@
 
 	GLenum generateMipmapHint;
 	GLenum fragmentShaderDerivativeHint;
+	GLenum textureFilteringHint;
 
 	GLint viewportX;
 	GLint viewportY;
@@ -489,6 +492,7 @@
 
 	void setGenerateMipmapHint(GLenum hint);
 	void setFragmentShaderDerivativeHint(GLenum hint);
+	void setTextureFilteringHint(GLenum hint);
 
 	void setViewportParams(GLint x, GLint y, GLsizei width, GLsizei height);
 
diff --git a/src/OpenGL/libGLESv2/libGLESv2.cpp b/src/OpenGL/libGLESv2/libGLESv2.cpp
index 920e1a0..dfe5959 100644
--- a/src/OpenGL/libGLESv2/libGLESv2.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv2.cpp
@@ -4247,6 +4247,9 @@
 	case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES:
 		if(context) context->setFragmentShaderDerivativeHint(mode);
 		break;
+	case GL_TEXTURE_FILTERING_HINT_CHROMIUM:
+		if(context) context->setTextureFilteringHint(mode);
+		break;
 	default:
 		return error(GL_INVALID_ENUM);
 	}
diff --git a/src/Renderer/PixelProcessor.cpp b/src/Renderer/PixelProcessor.cpp
index 172e8ef..db11aed 100644
--- a/src/Renderer/PixelProcessor.cpp
+++ b/src/Renderer/PixelProcessor.cpp
@@ -444,6 +444,15 @@
 		else ASSERT(false);
 	}
 
+	void PixelProcessor::setHighPrecisionFiltering(unsigned int sampler, bool highPrecisionFiltering)
+	{
+		if(sampler < TEXTURE_IMAGE_UNITS)
+		{
+			context->sampler[sampler].setHighPrecisionFiltering(highPrecisionFiltering);
+		}
+		else ASSERT(false);
+	}
+
 	void PixelProcessor::setSwizzleR(unsigned int sampler, SwizzleType swizzleR)
 	{
 		if(sampler < TEXTURE_IMAGE_UNITS)
diff --git a/src/Renderer/PixelProcessor.hpp b/src/Renderer/PixelProcessor.hpp
index 94d52d3..dd54b72 100644
--- a/src/Renderer/PixelProcessor.hpp
+++ b/src/Renderer/PixelProcessor.hpp
@@ -231,6 +231,7 @@
 		void setMipmapLOD(unsigned int sampler, float bias);
 		void setBorderColor(unsigned int sampler, const Color<float> &borderColor);
 		void setMaxAnisotropy(unsigned int sampler, float maxAnisotropy);
+		void setHighPrecisionFiltering(unsigned int sampler, bool highPrecisionFiltering);
 		void setSwizzleR(unsigned int sampler, SwizzleType swizzleR);
 		void setSwizzleG(unsigned int sampler, SwizzleType swizzleG);
 		void setSwizzleB(unsigned int sampler, SwizzleType swizzleB);
diff --git a/src/Renderer/Renderer.cpp b/src/Renderer/Renderer.cpp
index a84423d..a67ba22 100644
--- a/src/Renderer/Renderer.cpp
+++ b/src/Renderer/Renderer.cpp
@@ -2314,6 +2314,18 @@
 		}
 	}
 
+	void Renderer::setHighPrecisionFiltering(SamplerType type, int sampler, bool highPrecisionFiltering)
+	{
+		if(type == SAMPLER_PIXEL)
+		{
+			PixelProcessor::setHighPrecisionFiltering(sampler, highPrecisionFiltering);
+		}
+		else
+		{
+			VertexProcessor::setHighPrecisionFiltering(sampler, highPrecisionFiltering);
+		}
+	}
+
 	void Renderer::setSwizzleR(SamplerType type, int sampler, SwizzleType swizzleR)
 	{
 		if(type == SAMPLER_PIXEL)
diff --git a/src/Renderer/Renderer.hpp b/src/Renderer/Renderer.hpp
index c59dd31..c29020f 100644
--- a/src/Renderer/Renderer.hpp
+++ b/src/Renderer/Renderer.hpp
@@ -345,6 +345,7 @@
 		void setMipmapLOD(SamplerType type, int sampler, float bias);
 		void setBorderColor(SamplerType type, int sampler, const Color<float> &borderColor);
 		void setMaxAnisotropy(SamplerType type, int sampler, float maxAnisotropy);
+		void setHighPrecisionFiltering(SamplerType type, int sampler, bool highPrecisionFiltering);
 		void setSwizzleR(SamplerType type, int sampler, SwizzleType swizzleR);
 		void setSwizzleG(SamplerType type, int sampler, SwizzleType swizzleG);
 		void setSwizzleB(SamplerType type, int sampler, SwizzleType swizzleB);
diff --git a/src/Renderer/Sampler.cpp b/src/Renderer/Sampler.cpp
index e2447e0..7d7d47c 100644
--- a/src/Renderer/Sampler.cpp
+++ b/src/Renderer/Sampler.cpp
@@ -60,6 +60,7 @@
 		mipmapFilterState = MIPMAP_NONE;
 		sRGB = false;
 		gather = false;
+		highPrecisionFiltering = false;
 
 		swizzleR = SWIZZLE_RED;
 		swizzleG = SWIZZLE_GREEN;
@@ -97,6 +98,7 @@
 			state.swizzleG = swizzleG;
 			state.swizzleB = swizzleB;
 			state.swizzleA = swizzleA;
+			state.highPrecisionFiltering = highPrecisionFiltering;
 
 			#if PERF_PROFILE
 				state.compressedFormat = Surface::isCompressed(externalTextureFormat);
@@ -298,6 +300,11 @@
 		texture.maxAnisotropy = maxAnisotropy;
 	}
 
+	void Sampler::setHighPrecisionFiltering(bool highPrecisionFiltering)
+	{
+		this->highPrecisionFiltering = highPrecisionFiltering;
+	}
+
 	void Sampler::setSwizzleR(SwizzleType swizzleR)
 	{
 		this->swizzleR = swizzleR;
diff --git a/src/Renderer/Sampler.hpp b/src/Renderer/Sampler.hpp
index 4c4973d..288f179 100644
--- a/src/Renderer/Sampler.hpp
+++ b/src/Renderer/Sampler.hpp
@@ -140,6 +140,7 @@
 			SwizzleType swizzleG           : BITS(SWIZZLE_LAST);
 			SwizzleType swizzleB           : BITS(SWIZZLE_LAST);
 			SwizzleType swizzleA           : BITS(SWIZZLE_LAST);
+			bool highPrecisionFiltering    : 1;
 
 			#if PERF_PROFILE
 			bool compressedFormat          : 1;
@@ -163,6 +164,7 @@
 		void setReadSRGB(bool sRGB);
 		void setBorderColor(const Color<float> &borderColor);
 		void setMaxAnisotropy(float maxAnisotropy);
+		void setHighPrecisionFiltering(bool highPrecisionFiltering);
 		void setSwizzleR(SwizzleType swizzleR);
 		void setSwizzleG(SwizzleType swizzleG);
 		void setSwizzleB(SwizzleType swizzleB);
@@ -202,6 +204,7 @@
 		MipmapType mipmapFilterState;
 		bool sRGB;
 		bool gather;
+		bool highPrecisionFiltering;
 
 		SwizzleType swizzleR;
 		SwizzleType swizzleG;
diff --git a/src/Renderer/VertexProcessor.cpp b/src/Renderer/VertexProcessor.cpp
index 91c4d34..6972d94 100644
--- a/src/Renderer/VertexProcessor.cpp
+++ b/src/Renderer/VertexProcessor.cpp
@@ -602,6 +602,15 @@
 		else ASSERT(false);
 	}
 
+	void VertexProcessor::setHighPrecisionFiltering(unsigned int sampler, bool highPrecisionFiltering)
+	{
+		if(sampler < TEXTURE_IMAGE_UNITS)
+		{
+			context->sampler[sampler].setHighPrecisionFiltering(highPrecisionFiltering);
+		}
+		else ASSERT(false);
+	}
+
 	void VertexProcessor::setSwizzleR(unsigned int sampler, SwizzleType swizzleR)
 	{
 		if(sampler < VERTEX_TEXTURE_IMAGE_UNITS)
diff --git a/src/Renderer/VertexProcessor.hpp b/src/Renderer/VertexProcessor.hpp
index 278c9b1..3552f84 100644
--- a/src/Renderer/VertexProcessor.hpp
+++ b/src/Renderer/VertexProcessor.hpp
@@ -258,6 +258,7 @@
 		void setMipmapLOD(unsigned int sampler, float bias);
 		void setBorderColor(unsigned int sampler, const Color<float> &borderColor);
 		void setMaxAnisotropy(unsigned int stage, float maxAnisotropy);
+		void setHighPrecisionFiltering(unsigned int sampler, bool highPrecisionFiltering);
 		void setSwizzleR(unsigned int sampler, SwizzleType swizzleR);
 		void setSwizzleG(unsigned int sampler, SwizzleType swizzleG);
 		void setSwizzleB(unsigned int sampler, SwizzleType swizzleB);
diff --git a/src/Shader/SamplerCore.cpp b/src/Shader/SamplerCore.cpp
index 8c20f58..9bd977e 100644
--- a/src/Shader/SamplerCore.cpp
+++ b/src/Shader/SamplerCore.cpp
@@ -314,7 +314,7 @@
 		}
 		else
 		{
-			if(hasFloatTexture())   // FIXME: Mostly identical to integer sampling
+			if(hasFloatTexture() || state.highPrecisionFiltering)   // FIXME: Mostly identical to integer sampling
 			{
 				Float4 uuuu = u;
 				Float4 vvvv = v;