Refactor Blitter state.

Change-Id: Ife3342c64dfc846f8b7722f2e80612fc71f93688
Reviewed-on: https://swiftshader-review.googlesource.com/15128
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Renderer/Blitter.cpp b/src/Renderer/Blitter.cpp
index e41b937..dffc10f 100644
--- a/src/Renderer/Blitter.cpp
+++ b/src/Renderer/Blitter.cpp
@@ -22,7 +22,7 @@
 {
 	Blitter::Blitter()
 	{
-		blitCache = new RoutineCache<BlitState>(1024);
+		blitCache = new RoutineCache<State>(1024);
 	}
 
 	Blitter::~Blitter()
@@ -38,9 +38,8 @@
 		}
 
 		sw::Surface *color = sw::Surface::create(1, 1, 1, format, pixel, sw::Surface::bytes(format), sw::Surface::bytes(format));
-		Blitter::Options clearOptions = static_cast<sw::Blitter::Options>((rgbaMask & 0xF) | CLEAR_OPERATION);
 		SliceRectF sRect((float)dRect.x0, (float)dRect.y0, (float)dRect.x1, (float)dRect.y1, 0);
-		blit(color, sRect, dest, dRect, clearOptions);
+		blit(color, sRect, dest, dRect, {rgbaMask});
 		delete color;
 	}
 
@@ -133,20 +132,6 @@
 		return true;
 	}
 
-	void Blitter::blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil)
-	{
-		Blitter::Options options = WRITE_RGBA;
-		if(filter)
-		{
-			options = static_cast<Blitter::Options>(options | FILTER_LINEAR);
-		}
-		if(isStencil)
-		{
-			options = static_cast<Blitter::Options>(options | USE_STENCIL);
-		}
-		blit(source, sRect, dest, dRect, options);
-	}
-
 	void Blitter::blit(Surface *source, const SliceRectF &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options& options)
 	{
 		if(dest->getInternalFormat() == FORMAT_NULL)
@@ -192,7 +177,7 @@
 			for(int i = dRect.x0; i < dRect.x1; i++)
 			{
 				// FIXME: Support RGBA mask
-				dest->copyInternal(source, i, j, x, y, (options & FILTER_LINEAR) == FILTER_LINEAR);
+				dest->copyInternal(source, i, j, x, y, options.filter);
 
 				x += w;
 			}
@@ -234,11 +219,11 @@
 		dest->unlockInternal();
 	}
 
-	bool Blitter::read(Float4 &c, Pointer<Byte> element, Format format)
+	bool Blitter::read(Float4 &c, Pointer<Byte> element, const State &state)
 	{
 		c = Float4(0.0f, 0.0f, 0.0f, 1.0f);
 
-		switch(format)
+		switch(state.sourceFormat)
 		{
 		case FORMAT_L8:
 			c.xyz = Float(Int(*Pointer<Byte>(element)));
@@ -430,15 +415,15 @@
 		return true;
 	}
 
-	bool Blitter::write(Float4 &c, Pointer<Byte> element, Format format, const Blitter::Options& options)
+	bool Blitter::write(Float4 &c, Pointer<Byte> element, const State &state)
 	{
-		bool writeR = (options & WRITE_RED) == WRITE_RED;
-		bool writeG = (options & WRITE_GREEN) == WRITE_GREEN;
-		bool writeB = (options & WRITE_BLUE) == WRITE_BLUE;
-		bool writeA = (options & WRITE_ALPHA) == WRITE_ALPHA;
+		bool writeR = state.writeRed;
+		bool writeG = state.writeGreen;
+		bool writeB = state.writeBlue;
+		bool writeA = state.writeAlpha;
 		bool writeRGBA = writeR && writeG && writeB && writeA;
 
-		switch(format)
+		switch(state.destFormat)
 		{
 		case FORMAT_L8:
 			*Pointer<Byte>(element) = Byte(RoundInt(Float(c.x)));
@@ -553,7 +538,7 @@
 			if(writeA) { *Pointer<SByte>(element + 3) = SByte(RoundInt(Float(c.w))); }
 		case FORMAT_X8B8G8R8I:
 		case FORMAT_X8B8G8R8I_SNORM:
-			if(writeA && (format == FORMAT_X8B8G8R8I || format == FORMAT_X8B8G8R8I_SNORM))
+			if(writeA && (state.destFormat == FORMAT_X8B8G8R8I || state.destFormat == FORMAT_X8B8G8R8I_SNORM))
 			{
 				*Pointer<SByte>(element + 3) = SByte(0x7F);
 			}
@@ -568,7 +553,7 @@
 		case FORMAT_A8B8G8R8UI:
 			if(writeA) { *Pointer<Byte>(element + 3) = Byte(RoundInt(Float(c.w))); }
 		case FORMAT_X8B8G8R8UI:
-			if(writeA && (format == FORMAT_X8B8G8R8UI))
+			if(writeA && (state.destFormat == FORMAT_X8B8G8R8UI))
 			{
 				*Pointer<Byte>(element + 3) = Byte(0xFF);
 			}
@@ -794,11 +779,11 @@
 		return true;
 	}
 
-	bool Blitter::read(Int4 &c, Pointer<Byte> element, Format format)
+	bool Blitter::read(Int4 &c, Pointer<Byte> element, const State &state)
 	{
 		c = Int4(0, 0, 0, 1);
 
-		switch(format)
+		switch(state.sourceFormat)
 		{
 		case FORMAT_A8B8G8R8I:
 			c = Insert(c, Int(*Pointer<SByte>(element + 3)), 3);
@@ -857,20 +842,20 @@
 		return true;
 	}
 
-	bool Blitter::write(Int4 &c, Pointer<Byte> element, Format format, const Blitter::Options& options)
+	bool Blitter::write(Int4 &c, Pointer<Byte> element, const State &state)
 	{
-		bool writeR = (options & WRITE_RED) == WRITE_RED;
-		bool writeG = (options & WRITE_GREEN) == WRITE_GREEN;
-		bool writeB = (options & WRITE_BLUE) == WRITE_BLUE;
-		bool writeA = (options & WRITE_ALPHA) == WRITE_ALPHA;
+		bool writeR = state.writeRed;
+		bool writeG = state.writeGreen;
+		bool writeB = state.writeBlue;
+		bool writeA = state.writeAlpha;
 		bool writeRGBA = writeR && writeG && writeB && writeA;
 
-		switch(format)
+		switch(state.destFormat)
 		{
 		case FORMAT_A8B8G8R8I:
 			if(writeA) { *Pointer<SByte>(element + 3) = SByte(Extract(c, 3)); }
 		case FORMAT_X8B8G8R8I:
-			if(writeA && (format != FORMAT_A8B8G8R8I))
+			if(writeA && (state.destFormat != FORMAT_A8B8G8R8I))
 			{
 				*Pointer<SByte>(element + 3) = SByte(0x7F);
 			}
@@ -883,7 +868,7 @@
 		case FORMAT_A8B8G8R8UI:
 			if(writeA) { *Pointer<Byte>(element + 3) = Byte(Extract(c, 3)); }
 		case FORMAT_X8B8G8R8UI:
-			if(writeA && (format != FORMAT_A8B8G8R8UI))
+			if(writeA && (state.destFormat != FORMAT_A8B8G8R8UI))
 			{
 				*Pointer<Byte>(element + 3) = Byte(0xFF);
 			}
@@ -896,7 +881,7 @@
 		case FORMAT_A16B16G16R16I:
 			if(writeA) { *Pointer<Short>(element + 6) = Short(Extract(c, 3)); }
 		case FORMAT_X16B16G16R16I:
-			if(writeA && (format != FORMAT_A16B16G16R16I))
+			if(writeA && (state.destFormat != FORMAT_A16B16G16R16I))
 			{
 				*Pointer<Short>(element + 6) = Short(0x7FFF);
 			}
@@ -909,7 +894,7 @@
 		case FORMAT_A16B16G16R16UI:
 			if(writeA) { *Pointer<UShort>(element + 6) = UShort(Extract(c, 3)); }
 		case FORMAT_X16B16G16R16UI:
-			if(writeA && (format != FORMAT_A16B16G16R16UI))
+			if(writeA && (state.destFormat != FORMAT_A16B16G16R16UI))
 			{
 				*Pointer<UShort>(element + 6) = UShort(0xFFFF);
 			}
@@ -992,7 +977,7 @@
 		return true;
 	}
 
-	bool Blitter::GetScale(float4& scale, Format format)
+	bool Blitter::GetScale(float4 &scale, Format format)
 	{
 		switch(format)
 		{
@@ -1085,12 +1070,12 @@
 		return true;
 	}
 
-	bool Blitter::ApplyScaleAndClamp(Float4& value, const BlitState& state)
+	bool Blitter::ApplyScaleAndClamp(Float4 &value, const State  &state)
 	{
 		float4 scale, unscale;
-		if(Surface::isNonNormalizedInteger(state.sourceFormat) &&
-		   !Surface::isNonNormalizedInteger(state.destFormat) &&
-		   (state.options & CLEAR_OPERATION))
+		if(state.clearOperation &&
+		   Surface::isNonNormalizedInteger(state.sourceFormat) &&
+		   !Surface::isNonNormalizedInteger(state.destFormat))
 		{
 			// If we're clearing a buffer from an int or uint color into a normalized color,
 			// then the whole range of the int or uint color must be scaled between 0 and 1.
@@ -1134,13 +1119,21 @@
 		return true;
 	}
 
-	Int Blitter::ComputeOffset(Int& x, Int& y, Int& pitchB, int bytes, bool quadLayout)
+	Int Blitter::ComputeOffset(Int &x, Int &y, Int &pitchB, int bytes, bool quadLayout)
 	{
-		return (quadLayout ? (y & Int(~1)) : RValue<Int>(y)) * pitchB +
-		       (quadLayout ? ((y & Int(1)) << 1) + (x * 2) - (x & Int(1)) : RValue<Int>(x)) * bytes;
+		if(!quadLayout)
+		{
+			return y * pitchB + x * bytes;
+		}
+		else
+		{
+			// (x & ~1) * 2 + (x & 1) == (x - (x & 1)) * 2 + (x & 1) == x * 2 - (x & 1) * 2 + (x & 1) == x * 2 - (x & 1)
+			return (y & Int(~1)) * pitchB +
+			       ((y & Int(1)) * 2 + x * 2 - (x & Int(1))) * bytes;
+		}
 	}
 
-	Routine *Blitter::generate(BlitState &state)
+	Routine *Blitter::generate(const State &state)
 	{
 		Function<Void(Pointer<Byte>)> function;
 		{
@@ -1176,11 +1169,11 @@
 			Int4 constantColorI;
 			bool hasConstantColorF = false;
 			Float4 constantColorF;
-			if(state.options & CLEAR_OPERATION)
+			if(state.clearOperation)
 			{
 				if(intBoth) // Integer types
 				{
-					if(!read(constantColorI, source, state.sourceFormat))
+					if(!read(constantColorI, source, state))
 					{
 						return nullptr;
 					}
@@ -1188,7 +1181,7 @@
 				}
 				else
 				{
-					if(!read(constantColorF, source, state.sourceFormat))
+					if(!read(constantColorF, source, state))
 					{
 						return nullptr;
 					}
@@ -1214,14 +1207,14 @@
 
 					if(hasConstantColorI)
 					{
-						if(!write(constantColorI, d, state.destFormat, state.options))
+						if(!write(constantColorI, d, state))
 						{
 							return nullptr;
 						}
 					}
 					else if(hasConstantColorF)
 					{
-						if(!write(constantColorF, d, state.destFormat, state.options))
+						if(!write(constantColorF, d, state))
 						{
 							return nullptr;
 						}
@@ -1234,12 +1227,12 @@
 
 						Pointer<Byte> s = source + ComputeOffset(X, Y, sPitchB, srcBytes, srcQuadLayout);
 
-						if(!read(color, s, state.sourceFormat))
+						if(!read(color, s, state))
 						{
 							return nullptr;
 						}
 
-						if(!write(color, d, state.destFormat, state.options))
+						if(!write(color, d, state))
 						{
 							return nullptr;
 						}
@@ -1248,14 +1241,14 @@
 					{
 						Float4 color;
 
-						if(!(state.options & FILTER_LINEAR) || intSrc)
+						if(!state.filter || intSrc)
 						{
 							Int X = Int(x);
 							Int Y = Int(y);
 
 							Pointer<Byte> s = source + ComputeOffset(X, Y, sPitchB, srcBytes, srcQuadLayout);
 
-							if(!read(color, s, state.sourceFormat))
+							if(!read(color, s, state))
 							{
 								return nullptr;
 							}
@@ -1278,10 +1271,10 @@
 							Pointer<Byte> s10 = source + ComputeOffset(X0, Y1, sPitchB, srcBytes, srcQuadLayout);
 							Pointer<Byte> s11 = source + ComputeOffset(X1, Y1, sPitchB, srcBytes, srcQuadLayout);
 
-							Float4 c00; if(!read(c00, s00, state.sourceFormat)) return nullptr;
-							Float4 c01; if(!read(c01, s01, state.sourceFormat)) return nullptr;
-							Float4 c10; if(!read(c10, s10, state.sourceFormat)) return nullptr;
-							Float4 c11; if(!read(c11, s11, state.sourceFormat)) return nullptr;
+							Float4 c00; if(!read(c00, s00, state)) return nullptr;
+							Float4 c01; if(!read(c01, s01, state)) return nullptr;
+							Float4 c10; if(!read(c10, s10, state)) return nullptr;
+							Float4 c11; if(!read(c11, s11, state)) return nullptr;
 
 							Float4 fx = Float4(x0 - Float(X0));
 							Float4 fy = Float4(y0 - Float(Y0));
@@ -1299,7 +1292,7 @@
 
 						for(int s = 0; s < state.destSamples; s++)
 						{
-							if(!write(color, d, state.destFormat, state.options))
+							if(!write(color, d, state))
 							{
 								return nullptr;
 							}
@@ -1318,9 +1311,9 @@
 		return function(L"BlitRoutine");
 	}
 
-	bool Blitter::blitReactor(Surface *source, const SliceRectF &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options& options)
+	bool Blitter::blitReactor(Surface *source, const SliceRectF &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options &options)
 	{
-		ASSERT(!(options & CLEAR_OPERATION) || ((source->getWidth() == 1) && (source->getHeight() == 1) && (source->getDepth() == 1)));
+		ASSERT(!options.clearOperation || ((source->getWidth() == 1) && (source->getHeight() == 1) && (source->getDepth() == 1)));
 
 		Rect dRect = destRect;
 		RectF sRect = sourceRect;
@@ -1335,16 +1328,15 @@
 			swap(sRect.y0, sRect.y1);
 		}
 
-		BlitState state;
+		State state(options);
 
 		bool useSourceInternal = !source->isExternalDirty();
 		bool useDestInternal = !dest->isExternalDirty();
-		bool isStencil = ((options & USE_STENCIL) == USE_STENCIL);
+		bool isStencil = options.useStencil;
 
 		state.sourceFormat = isStencil ? source->getStencilFormat() : source->getFormat(useSourceInternal);
 		state.destFormat = isStencil ? dest->getStencilFormat() : dest->getFormat(useDestInternal);
 		state.destSamples = dest->getSamples();
-		state.options = options;
 
 		criticalSection.lock();
 		Routine *blitRoutine = blitCache->query(state);
@@ -1368,7 +1360,7 @@
 
 		BlitData data;
 
-		bool isRGBA = ((options & WRITE_RGBA) == WRITE_RGBA);
+		bool isRGBA = options.writeMask == 0xF;
 		bool isEntireDest = dest->isEntire(destRect);
 
 		data.source = isStencil ? source->lockStencil(0, 0, 0, sw::PUBLIC) :
diff --git a/src/Renderer/Blitter.hpp b/src/Renderer/Blitter.hpp
index 6e87db8..66ef1f8 100644
--- a/src/Renderer/Blitter.hpp
+++ b/src/Renderer/Blitter.hpp
@@ -25,30 +25,45 @@
 {
 	class Blitter
 	{
-		enum Options : unsigned char
+		struct Options
 		{
-			FILTER_POINT = 0x00,
-			WRITE_RED = 0x01,
-			WRITE_GREEN = 0x02,
-			WRITE_BLUE = 0x04,
-			WRITE_ALPHA = 0x08,
-			WRITE_RGBA = WRITE_RED | WRITE_GREEN | WRITE_BLUE | WRITE_ALPHA,
-			FILTER_LINEAR = 0x10,
-			CLEAR_OPERATION = 0x20,
-			USE_STENCIL = 0x40,
+			Options() {}
+			Options(bool filter, bool useStencil)
+				: writeMask(0xF), clearOperation(false), filter(filter), useStencil(useStencil) {}
+			Options(unsigned int writeMask)
+				: writeMask(writeMask), clearOperation(true), filter(false), useStencil(false) {}
+
+			union
+			{
+				struct
+				{
+					bool writeRed : 1;
+					bool writeGreen : 1;
+					bool writeBlue : 1;
+					bool writeAlpha : 1;
+				};
+
+				unsigned char writeMask;
+			};
+
+			bool clearOperation : 1;
+			bool filter : 1;
+			bool useStencil : 1;
 		};
 
-		struct BlitState
+		struct State : Options
 		{
-			bool operator==(const BlitState &state) const
+			State() {}
+			State(const Options &options) : Options(options) {}
+
+			bool operator==(const State &state) const
 			{
-				return memcmp(this, &state, sizeof(BlitState)) == 0;
+				return memcmp(this, &state, sizeof(State)) == 0;
 			}
 
 			Format sourceFormat;
 			Format destFormat;
 			int destSamples;
-			Blitter::Options options;
 		};
 
 		struct BlitData
@@ -78,24 +93,23 @@
 		virtual ~Blitter();
 
 		void clear(void *pixel, sw::Format format, Surface *dest, const SliceRect &dRect, unsigned int rgbaMask);
-		void blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil = false);
+		void blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Options &options);
 		void blit3D(Surface *source, Surface *dest);
 
 	private:
 		bool fastClear(void *pixel, sw::Format format, Surface *dest, const SliceRect &dRect, unsigned int rgbaMask);
 
-		bool read(Float4 &color, Pointer<Byte> element, Format format);
-		bool write(Float4 &color, Pointer<Byte> element, Format format, const Blitter::Options& options);
-		bool read(Int4 &color, Pointer<Byte> element, Format format);
-		bool write(Int4 &color, Pointer<Byte> element, Format format, const Blitter::Options& options);
+		bool read(Float4 &color, Pointer<Byte> element, const State &state);
+		bool write(Float4 &color, Pointer<Byte> element, const State &state);
+		bool read(Int4 &color, Pointer<Byte> element, const State &state);
+		bool write(Int4 &color, Pointer<Byte> element, const State &state);
 		static bool GetScale(float4& scale, Format format);
-		static bool ApplyScaleAndClamp(Float4& value, const BlitState& state);
-		static Int ComputeOffset(Int& x, Int& y, Int& pitchB, int bytes, bool quadLayout);
-		void blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Blitter::Options& options);
-		bool blitReactor(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Blitter::Options& options);
-		Routine *generate(BlitState &state);
+		static bool ApplyScaleAndClamp(Float4 &value, const State &state);
+		static Int ComputeOffset(Int &x, Int &y, Int &pitchB, int bytes, bool quadLayout);
+		bool blitReactor(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Options &options);
+		Routine *generate(const State &state);
 
-		RoutineCache<BlitState> *blitCache;
+		RoutineCache<State> *blitCache;
 		MutexLock criticalSection;
 	};
 }
diff --git a/src/Renderer/Renderer.cpp b/src/Renderer/Renderer.cpp
index 1c92576..ffdb547 100644
--- a/src/Renderer/Renderer.cpp
+++ b/src/Renderer/Renderer.cpp
@@ -684,7 +684,7 @@
 
 	void Renderer::blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil)
 	{
-		blitter->blit(source, sRect, dest, dRect, filter, isStencil);
+		blitter->blit(source, sRect, dest, dRect, {filter, isStencil});
 	}
 
 	void Renderer::blit3D(Surface *source, Surface *dest)
diff --git a/src/Shader/PixelRoutine.cpp b/src/Shader/PixelRoutine.cpp
index 949fbfc..a5b6f56 100644
--- a/src/Shader/PixelRoutine.cpp
+++ b/src/Shader/PixelRoutine.cpp
@@ -991,7 +991,7 @@
 
 	bool PixelRoutine::isSRGB(int index) const
 	{
-		return state.targetFormat[index] == FORMAT_SRGB8_A8 || state.targetFormat[index] == FORMAT_SRGB8_X8;
+		return Surface::isSRGBformat(state.targetFormat[index]);
 	}
 
 	void PixelRoutine::readPixel(int index, Pointer<Byte> &cBuffer, Int &x, Vector4s &pixel)
@@ -2653,17 +2653,12 @@
 
 	void PixelRoutine::sRGBtoLinear16_12_16(Vector4s &c)
 	{
+		Pointer<Byte> LUT = constants + OFFSET(Constants,sRGBtoLinear12_16);
+
 		c.x = As<UShort4>(c.x) >> 4;
 		c.y = As<UShort4>(c.y) >> 4;
 		c.z = As<UShort4>(c.z) >> 4;
 
-		sRGBtoLinear12_16(c);
-	}
-
-	void PixelRoutine::sRGBtoLinear12_16(Vector4s &c)
-	{
-		Pointer<Byte> LUT = constants + OFFSET(Constants,sRGBtoLinear12_16);
-
 		c.x = Insert(c.x, *Pointer<Short>(LUT + 2 * Int(Extract(c.x, 0))), 0);
 		c.x = Insert(c.x, *Pointer<Short>(LUT + 2 * Int(Extract(c.x, 1))), 1);
 		c.x = Insert(c.x, *Pointer<Short>(LUT + 2 * Int(Extract(c.x, 2))), 2);
diff --git a/src/Shader/PixelRoutine.hpp b/src/Shader/PixelRoutine.hpp
index bc558c2..1cd076e 100644
--- a/src/Shader/PixelRoutine.hpp
+++ b/src/Shader/PixelRoutine.hpp
@@ -83,7 +83,6 @@
 		void writeDepth(Pointer<Byte> &zBuffer, int q, Int &x, Float4 &z, Int &zMask);
 
 		void sRGBtoLinear16_12_16(Vector4s &c);
-		void sRGBtoLinear12_16(Vector4s &c);
 		void linearToSRGB16_12_16(Vector4s &c);
 		Float4 sRGBtoLinear(const Float4 &x);