Fix eglChooseConfig behavior to match CTS expectation

When multiple instances of the same attribute are provided, CTS expects
the last instance to win. Achieve this by:
	1/ Reducing the attribute list in getConfigs()
	2/ Adjusting the sort order's consideration of which color
	components are "required" to allow a later 0 or EGL_DONT_CARE to
	remove a component from consideration again.

Affects: dEQP-EGL.functional.choose_config.*
Bug: b/113679786

V2: Fix up the alignment I broke
V4: Style fix

Change-Id: If9375c9dc5aaadb0ee7b77ecf1e1098c82fa30ca
Reviewed-on: https://swiftshader-review.googlesource.com/20448
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Chris Forbes <chrisforbes@google.com>
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 81d1149..b03eaa4 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -25,6 +25,7 @@
  Daniel Toyama <kenjitoyama@google.com>

  Meng-Lin Wu <marleymoo@google.com>

  Krzysztof KosiƄski <krzysio@google.com>

+ Chris Forbes <chrisforbes@google.com>

 

 TransGaming Inc.

  Nicolas Capens

diff --git a/src/OpenGL/libEGL/Config.cpp b/src/OpenGL/libEGL/Config.cpp
index 66f4260..4c35f6b 100644
--- a/src/OpenGL/libEGL/Config.cpp
+++ b/src/OpenGL/libEGL/Config.cpp
@@ -29,6 +29,7 @@
 #include <algorithm>
 #include <cstring>
 #include <vector>
+#include <map>
 
 using namespace std;
 
@@ -261,16 +262,15 @@
 	// not considering components that are 0 or don't-care.
 	for(const EGLint *attr = attribList; attr[0] != EGL_NONE; attr += 2)
 	{
-		if(attr[1] != 0 && attr[1] != EGL_DONT_CARE)
+		// When multiple instances of the same attribute are present, last wins.
+		bool isSpecified = attr[1] && attr[1] != EGL_DONT_CARE;
+		switch(attr[0])
 		{
-			switch(attr[0])
-			{
-			case EGL_RED_SIZE:       mWantRed = true;       break;
-			case EGL_GREEN_SIZE:     mWantGreen = true;     break;
-			case EGL_BLUE_SIZE:      mWantBlue = true;      break;
-			case EGL_ALPHA_SIZE:     mWantAlpha = true;     break;
-			case EGL_LUMINANCE_SIZE: mWantLuminance = true; break;
-			}
+			case EGL_RED_SIZE:       mWantRed = isSpecified;       break;
+			case EGL_GREEN_SIZE:     mWantGreen = isSpecified;     break;
+			case EGL_BLUE_SIZE:      mWantBlue = isSpecified;      break;
+			case EGL_ALPHA_SIZE:     mWantAlpha = isSpecified;     break;
+			case EGL_LUMINANCE_SIZE: mWantLuminance = isSpecified; break;
 		}
 	}
 }
@@ -344,49 +344,60 @@
 	vector<const Config*> passed;
 	passed.reserve(mSet.size());
 
+	/* Conformance expects for multiple instances of the same attribute that the
+	 * last instance `wins`. Reduce the attribute list first to comply with
+	 * this.
+	 */
+	/* TODO: C++11: unordered_map would be fine here */
+	map<EGLint, EGLint> attribs;
+	const EGLint *attribute = attribList;
+	while (attribute[0] != EGL_NONE)
+	{
+		attribs[attribute[0]] = attribute[1];
+		attribute += 2;
+	}
+
 	for(Iterator config = mSet.begin(); config != mSet.end(); config++)
 	{
 		bool match = true;
 		bool caveatMatch = (config->mConfigCaveat == EGL_NONE);
-		const EGLint *attribute = attribList;
-
-		while(attribute[0] != EGL_NONE)
+		for (map<EGLint, EGLint>::iterator attribIt = attribs.begin(); attribIt != attribs.end(); attribIt++)
 		{
-			if(attribute[1] != EGL_DONT_CARE)
+			if(attribIt->second != EGL_DONT_CARE)
 			{
-				switch(attribute[0])
+				switch(attribIt->first)
 				{
-				case EGL_BUFFER_SIZE:                match = config->mBufferSize >= attribute[1];                           break;
-				case EGL_ALPHA_SIZE:                 match = config->mAlphaSize >= attribute[1];                            break;
-				case EGL_BLUE_SIZE:                  match = config->mBlueSize >= attribute[1];                             break;
-				case EGL_GREEN_SIZE:                 match = config->mGreenSize >= attribute[1];                            break;
-				case EGL_RED_SIZE:                   match = config->mRedSize >= attribute[1];                              break;
-				case EGL_DEPTH_SIZE:                 match = config->mDepthSize >= attribute[1];                            break;
-				case EGL_STENCIL_SIZE:               match = config->mStencilSize >= attribute[1];                          break;
-				case EGL_CONFIG_CAVEAT:              match = config->mConfigCaveat == (EGLenum)attribute[1];                break;
-				case EGL_CONFIG_ID:                  match = config->mConfigID == attribute[1];                             break;
-				case EGL_LEVEL:                      match = config->mLevel >= attribute[1];                                break;
-				case EGL_NATIVE_RENDERABLE:          match = config->mNativeRenderable == (EGLBoolean)attribute[1];         break;
-				case EGL_NATIVE_VISUAL_TYPE:         match = config->mNativeVisualType == attribute[1];                     break;
-				case EGL_SAMPLES:                    match = config->mSamples >= attribute[1];                              break;
-				case EGL_SAMPLE_BUFFERS:             match = config->mSampleBuffers >= attribute[1];                        break;
-				case EGL_SURFACE_TYPE:               match = (config->mSurfaceType & attribute[1]) == attribute[1];         break;
-				case EGL_TRANSPARENT_TYPE:           match = config->mTransparentType == (EGLenum)attribute[1];             break;
-				case EGL_TRANSPARENT_BLUE_VALUE:     match = config->mTransparentBlueValue == attribute[1];                 break;
-				case EGL_TRANSPARENT_GREEN_VALUE:    match = config->mTransparentGreenValue == attribute[1];                break;
-				case EGL_TRANSPARENT_RED_VALUE:      match = config->mTransparentRedValue == attribute[1];                  break;
-				case EGL_BIND_TO_TEXTURE_RGB:        match = config->mBindToTextureRGB == (EGLBoolean)attribute[1];         break;
-				case EGL_BIND_TO_TEXTURE_RGBA:       match = config->mBindToTextureRGBA == (EGLBoolean)attribute[1];        break;
-				case EGL_MIN_SWAP_INTERVAL:          match = config->mMinSwapInterval == attribute[1];                      break;
-				case EGL_MAX_SWAP_INTERVAL:          match = config->mMaxSwapInterval == attribute[1];                      break;
-				case EGL_LUMINANCE_SIZE:             match = config->mLuminanceSize >= attribute[1];                        break;
-				case EGL_ALPHA_MASK_SIZE:            match = config->mAlphaMaskSize >= attribute[1];                        break;
-				case EGL_COLOR_BUFFER_TYPE:          match = config->mColorBufferType == (EGLenum)attribute[1];             break;
-				case EGL_RENDERABLE_TYPE:            match = (config->mRenderableType & attribute[1]) == attribute[1];      break;
-				case EGL_MATCH_NATIVE_PIXMAP:        match = false; UNIMPLEMENTED();                                        break;
-				case EGL_CONFORMANT:                 match = (config->mConformant & attribute[1]) == attribute[1];          break;
-				case EGL_RECORDABLE_ANDROID:         match = config->mRecordableAndroid == (EGLBoolean)attribute[1];        break;
-				case EGL_FRAMEBUFFER_TARGET_ANDROID: match = config->mFramebufferTargetAndroid == (EGLBoolean)attribute[1]; break;
+				case EGL_BUFFER_SIZE:                match = config->mBufferSize >= attribIt->second;                           break;
+				case EGL_ALPHA_SIZE:                 match = config->mAlphaSize >= attribIt->second;                            break;
+				case EGL_BLUE_SIZE:                  match = config->mBlueSize >= attribIt->second;                             break;
+				case EGL_GREEN_SIZE:                 match = config->mGreenSize >= attribIt->second;                            break;
+				case EGL_RED_SIZE:                   match = config->mRedSize >= attribIt->second;                              break;
+				case EGL_DEPTH_SIZE:                 match = config->mDepthSize >= attribIt->second;                            break;
+				case EGL_STENCIL_SIZE:               match = config->mStencilSize >= attribIt->second;                          break;
+				case EGL_CONFIG_CAVEAT:              match = config->mConfigCaveat == (EGLenum)attribIt->second;                break;
+				case EGL_CONFIG_ID:                  match = config->mConfigID == attribIt->second;                             break;
+				case EGL_LEVEL:                      match = config->mLevel >= attribIt->second;                                break;
+				case EGL_NATIVE_RENDERABLE:          match = config->mNativeRenderable == (EGLBoolean)attribIt->second;         break;
+				case EGL_NATIVE_VISUAL_TYPE:         match = config->mNativeVisualType == attribIt->second;                     break;
+				case EGL_SAMPLES:                    match = config->mSamples >= attribIt->second;                              break;
+				case EGL_SAMPLE_BUFFERS:             match = config->mSampleBuffers >= attribIt->second;                        break;
+				case EGL_SURFACE_TYPE:               match = (config->mSurfaceType & attribIt->second) == attribIt->second;     break;
+				case EGL_TRANSPARENT_TYPE:           match = config->mTransparentType == (EGLenum)attribIt->second;             break;
+				case EGL_TRANSPARENT_BLUE_VALUE:     match = config->mTransparentBlueValue == attribIt->second;                 break;
+				case EGL_TRANSPARENT_GREEN_VALUE:    match = config->mTransparentGreenValue == attribIt->second;                break;
+				case EGL_TRANSPARENT_RED_VALUE:      match = config->mTransparentRedValue == attribIt->second;                  break;
+				case EGL_BIND_TO_TEXTURE_RGB:        match = config->mBindToTextureRGB == (EGLBoolean)attribIt->second;         break;
+				case EGL_BIND_TO_TEXTURE_RGBA:       match = config->mBindToTextureRGBA == (EGLBoolean)attribIt->second;        break;
+				case EGL_MIN_SWAP_INTERVAL:          match = config->mMinSwapInterval == attribIt->second;                      break;
+				case EGL_MAX_SWAP_INTERVAL:          match = config->mMaxSwapInterval == attribIt->second;                      break;
+				case EGL_LUMINANCE_SIZE:             match = config->mLuminanceSize >= attribIt->second;                        break;
+				case EGL_ALPHA_MASK_SIZE:            match = config->mAlphaMaskSize >= attribIt->second;                        break;
+				case EGL_COLOR_BUFFER_TYPE:          match = config->mColorBufferType == (EGLenum)attribIt->second;             break;
+				case EGL_RENDERABLE_TYPE:            match = (config->mRenderableType & attribIt->second) == attribIt->second;  break;
+				case EGL_MATCH_NATIVE_PIXMAP:        match = false; UNIMPLEMENTED();                                            break;
+				case EGL_CONFORMANT:                 match = (config->mConformant & attribIt->second) == attribIt->second;      break;
+				case EGL_RECORDABLE_ANDROID:         match = config->mRecordableAndroid == (EGLBoolean)attribIt->second;        break;
+				case EGL_FRAMEBUFFER_TARGET_ANDROID: match = config->mFramebufferTargetAndroid == (EGLBoolean)attribIt->second; break;
 
 				// Ignored attributes
 				case EGL_MAX_PBUFFER_WIDTH:
@@ -406,12 +417,10 @@
 				}
 			}
 
-			if(attribute[0] == EGL_CONFIG_CAVEAT)
+			if(attribIt->first == EGL_CONFIG_CAVEAT)
 			{
 				caveatMatch = match;
 			}
-
-			attribute += 2;
 		}
 
 		if(match && caveatMatch)   // We require the caveats to be NONE or the requested flags