Implement GL_NV_read_depth_stencil.

Bug swiftshader:104

Change-Id: I9fcea0c76875de735c3bad0a5c587875d0ca4e26
Reviewed-on: https://swiftshader-review.googlesource.com/18089
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Lingfeng Yang <lfy@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/OpenGL/common/Image.cpp b/src/OpenGL/common/Image.cpp
index cdded3a..d169fc9 100644
--- a/src/OpenGL/common/Image.cpp
+++ b/src/OpenGL/common/Image.cpp
@@ -160,6 +160,14 @@
 			default: UNREACHABLE(type);
 			}
 			break;
+		case GL_STENCIL_INDEX_OES:
+			switch(type)
+			{
+			case GL_UNSIGNED_BYTE: return sw::FORMAT_S8;
+			default: UNREACHABLE(type);
+			}
+			break;
+		case GL_DEPTH_STENCIL_OES:   // Cannot be read as one format. Handled separately.
 		default:
 			UNREACHABLE(format);
 			break;
diff --git a/src/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp
index fbbb356..612ee2a 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -3322,9 +3322,13 @@
 	egl::Image *renderTarget = nullptr;
 	switch(format)
 	{
-	case GL_DEPTH_COMPONENT:   // GL_NV_read_depth
+	case GL_DEPTH_COMPONENT:     // GL_NV_read_depth
+	case GL_DEPTH_STENCIL_OES:   // GL_NV_read_depth_stencil
 		renderTarget = framebuffer->getDepthBuffer();
 		break;
+	case GL_STENCIL_INDEX_OES:   // GL_NV_read_stencil
+		renderTarget = framebuffer->getStencilBuffer();
+		break;
 	default:
 		renderTarget = framebuffer->getReadRenderTarget();
 		break;
@@ -3339,11 +3343,64 @@
 	sw::SliceRect dstRect(0, 0, width, height, 0);
 	srcRect.clip(0.0f, 0.0f, (float)renderTarget->getWidth(), (float)renderTarget->getHeight());
 
-	sw::Surface *externalSurface = sw::Surface::create(width, height, 1, gl::ConvertReadFormatType(format, type), pixels, outputPitch, outputPitch  *  outputHeight);
-	device->blit(renderTarget, srcRect, externalSurface, dstRect, false, false, false);
-	externalSurface->lockExternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC);
-	externalSurface->unlockExternal();
-	delete externalSurface;
+	if(format != GL_DEPTH_STENCIL_OES)   // The blitter only handles reading either depth or stencil.
+	{
+		sw::Surface *externalSurface = sw::Surface::create(width, height, 1, gl::ConvertReadFormatType(format, type), pixels, outputPitch, outputPitch  *  outputHeight);
+		device->blit(renderTarget, srcRect, externalSurface, dstRect, false, false, false);
+		externalSurface->lockExternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC);
+		externalSurface->unlockExternal();
+		delete externalSurface;
+	}
+	else   // format == GL_DEPTH_STENCIL_OES
+	{
+		ASSERT(renderTarget->getInternalFormat() == sw::FORMAT_D32F_LOCKABLE);
+		float *depth = (float*)renderTarget->lockInternal((int)srcRect.x0, (int)srcRect.y0, 0, sw::LOCK_READONLY, sw::PUBLIC);
+		uint8_t *stencil = (uint8_t*)renderTarget->lockStencil((int)srcRect.x0, (int)srcRect.y0, 0, sw::PUBLIC);
+
+		switch(type)
+		{
+		case GL_UNSIGNED_INT_24_8_OES:
+			{
+				uint32_t *output = (uint32_t*)pixels;
+
+				for(int y = 0; y < height; y++)
+				{
+					for(int x = 0; x < width; x++)
+					{
+						output[x] = ((uint32_t)roundf(depth[x] * 0xFFFFFF00) & 0xFFFFFF00) | stencil[x];
+					}
+
+					depth += renderTarget->getInternalPitchP();
+					stencil += renderTarget->getStencilPitchB();
+					(uint8_t*&)output += outputPitch;
+				}
+			}
+			break;
+		case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
+			{
+				struct D32FS8 { float depth32f; unsigned int stencil24_8; };
+				D32FS8 *output = (D32FS8*)pixels;
+
+				for(int y = 0; y < height; y++)
+				{
+					for(int x = 0; x < width; x++)
+					{
+						output[x].depth32f = depth[x];
+						output[x].stencil24_8 = stencil[x];
+					}
+
+					depth += renderTarget->getInternalPitchP();
+					stencil += renderTarget->getStencilPitchB();
+					(uint8_t*&)output += outputPitch;
+				}
+			}
+			break;
+		default: UNREACHABLE(type);
+		}
+
+		renderTarget->unlockInternal();
+		renderTarget->unlockStencil();
+	}
 
 	renderTarget->release();
 }
@@ -4441,9 +4498,12 @@
 		"GL_APPLE_texture_format_BGRA8888",
 		"GL_CHROMIUM_color_buffer_float_rgba", // A subset of EXT_color_buffer_float on top of OpenGL ES 2.0
 		"GL_CHROMIUM_texture_filtering_hint",
+		"GL_NV_depth_buffer_float2",
 		"GL_NV_fence",
 		"GL_NV_framebuffer_blit",
 		"GL_NV_read_depth",
+		"GL_NV_read_depth_stencil",
+		"GL_NV_read_stencil",
 	};
 
 	GLuint numExtensions = sizeof(extensions) / sizeof(extensions[0]);
diff --git a/src/OpenGL/libGLESv2/utilities.cpp b/src/OpenGL/libGLESv2/utilities.cpp
index 4108a79..6ae091e 100644
--- a/src/OpenGL/libGLESv2/utilities.cpp
+++ b/src/OpenGL/libGLESv2/utilities.cpp
@@ -677,6 +677,70 @@
 			return true;
 		}
 
+		// GL_NV_read_depth_stencil
+		if(format == GL_DEPTH_STENCIL_OES)
+		{
+			Renderbuffer *depthbuffer = framebuffer->getDepthbuffer();
+
+			if(!depthbuffer)
+			{
+				return error(GL_INVALID_OPERATION, false);
+			}
+
+			GLint internalformat = depthbuffer->getFormat();
+
+			switch(type)
+			{
+			case GL_UNSIGNED_INT_24_8_OES:
+				switch(internalformat)
+				{
+				case GL_DEPTH24_STENCIL8:
+					break;
+				case GL_DEPTH32F_STENCIL8:
+					return error(GL_INVALID_OPERATION, false);
+				default:
+					UNREACHABLE(internalformat);
+					return error(GL_INVALID_OPERATION, false);
+				}
+			case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
+				switch(internalformat)
+				{
+				case GL_DEPTH32F_STENCIL8:
+					break;
+				case GL_DEPTH24_STENCIL8:
+					return error(GL_INVALID_OPERATION, false);
+				default:
+					UNREACHABLE(internalformat);
+					return error(GL_INVALID_OPERATION, false);
+				}
+			default:
+				return error(GL_INVALID_ENUM, false);
+			}
+
+			return true;
+		}
+
+		// GL_NV_read_stencil
+		if(format == GL_STENCIL_INDEX_OES)
+		{
+			Renderbuffer *stencilbuffer = framebuffer->getStencilbuffer();
+
+			if(!stencilbuffer)
+			{
+				return error(GL_INVALID_OPERATION, false);
+			}
+
+			switch(type)
+			{
+			case GL_UNSIGNED_BYTE:
+				break;
+			default:
+				return error(GL_INVALID_ENUM, false);
+			}
+
+			return true;
+		}
+
 		Renderbuffer *colorbuffer = framebuffer->getReadColorbuffer();
 
 		if(!colorbuffer)