glBlitFramebuffer support for depth/stencil formats

Added support for depth and stencil formats for glBlitFramebuffer:
- Blitter now supports quad layout and stencil
- Device::stretchRect() now supports specific buffers, so that a
  caller can specifically choose which buffer to copy

Change-Id: Iae0898df11e0a1d3c006113486ed15a3fd2f90a9
Reviewed-on: https://swiftshader-review.googlesource.com/7510
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp
index 6033efe..0e32336 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -3873,7 +3873,7 @@
 
 void Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                               GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
-                              GLbitfield mask, bool filter)
+                              GLbitfield mask, bool filter, bool allowPartialDepthStencilBlit)
 {
 	Framebuffer *readFramebuffer = getReadFramebuffer();
 	Framebuffer *drawFramebuffer = getDrawFramebuffer();
@@ -4048,7 +4048,8 @@
 	}
 
 	bool blitRenderTarget = false;
-	bool blitDepthStencil = false;
+	bool blitDepth = false;
+	bool blitStencil = false;
 
 	if(mask & GL_COLOR_BUFFER_BIT)
 	{
@@ -4083,7 +4084,7 @@
 					return error(GL_INVALID_OPERATION);
 				}
 
-				blitDepthStencil = true;
+				blitDepth = true;
 				readDSBuffer = readFramebuffer->getDepthbuffer();
 				drawDSBuffer = drawFramebuffer->getDepthbuffer();
 			}
@@ -4098,15 +4099,15 @@
 					return error(GL_INVALID_OPERATION);
 				}
 
-				blitDepthStencil = true;
+				blitStencil = true;
 				readDSBuffer = readFramebuffer->getStencilbuffer();
 				drawDSBuffer = drawFramebuffer->getStencilbuffer();
 			}
 		}
 
-		if(partialBufferCopy)
+		if(partialBufferCopy && !allowPartialDepthStencilBlit)
 		{
-			ERR("Only whole-buffer depth and stencil blits are supported by this implementation.");
+			ERR("Only whole-buffer depth and stencil blits are supported by ANGLE_framebuffer_blit.");
 			return error(GL_INVALID_OPERATION);   // Only whole-buffer copies are permitted
 		}
 
@@ -4117,7 +4118,7 @@
 		}
 	}
 
-	if(blitRenderTarget || blitDepthStencil)
+	if(blitRenderTarget || blitDepth || blitStencil)
 	{
 		if(blitRenderTarget)
 		{
@@ -4133,7 +4134,7 @@
 				swap(destRect.y0, destRect.y1);
 			}
 
-			bool success = device->stretchRect(readRenderTarget, &sourceRect, drawRenderTarget, &destRect, filter);
+			bool success = device->stretchRect(readRenderTarget, &sourceRect, drawRenderTarget, &destRect, (filter ? Device::USE_FILTER : 0) | Device::COLOR_BUFFER);
 
 			readRenderTarget->release();
 			drawRenderTarget->release();
@@ -4145,9 +4146,32 @@
 			}
 		}
 
-		if(blitDepthStencil)
+		if(blitDepth)
 		{
-			bool success = device->stretchRect(readFramebuffer->getDepthBuffer(), nullptr, drawFramebuffer->getDepthBuffer(), nullptr, false);
+			egl::Image *readRenderTarget = readFramebuffer->getDepthBuffer();
+			egl::Image *drawRenderTarget = drawFramebuffer->getDepthBuffer();
+
+			bool success = device->stretchRect(readRenderTarget, &sourceRect, drawRenderTarget, &destRect, (filter ? Device::USE_FILTER : 0) | Device::DEPTH_BUFFER);
+
+			readRenderTarget->release();
+			drawRenderTarget->release();
+
+			if(!success)
+			{
+				ERR("BlitFramebuffer failed.");
+				return;
+			}
+		}
+
+		if(blitStencil)
+		{
+			egl::Image *readRenderTarget = readFramebuffer->getStencilBuffer();
+			egl::Image *drawRenderTarget = drawFramebuffer->getStencilBuffer();
+
+			bool success = device->stretchRect(readRenderTarget, &sourceRect, drawRenderTarget, &destRect, (filter ? Device::USE_FILTER : 0) | Device::STENCIL_BUFFER);
+
+			readRenderTarget->release();
+			drawRenderTarget->release();
 
 			if(!success)
 			{
diff --git a/src/OpenGL/libGLESv2/Context.h b/src/OpenGL/libGLESv2/Context.h
index f638c05..cd8194f 100644
--- a/src/OpenGL/libGLESv2/Context.h
+++ b/src/OpenGL/libGLESv2/Context.h
@@ -684,7 +684,7 @@
 
 	void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
 	                     GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
-	                     GLbitfield mask, bool filter);
+	                     GLbitfield mask, bool filter, bool allowPartialDepthStencilBlit);
 
 	virtual void bindTexImage(egl::Surface *surface);
 	virtual EGLenum validateSharedImage(EGLenum target, GLuint name, GLuint textureLevel);
diff --git a/src/OpenGL/libGLESv2/Device.cpp b/src/OpenGL/libGLESv2/Device.cpp
index 225ceeb..2e7a2e3 100644
--- a/src/OpenGL/libGLESv2/Device.cpp
+++ b/src/OpenGL/libGLESv2/Device.cpp
@@ -510,7 +510,7 @@
 		}
 	}
 
-	bool Device::stretchRect(sw::Surface *source, const sw::SliceRect *sourceRect, sw::Surface *dest, const sw::SliceRect *destRect, bool filter)
+	bool Device::stretchRect(sw::Surface *source, const sw::SliceRect *sourceRect, sw::Surface *dest, const sw::SliceRect *destRect, unsigned char flags)
 	{
 		if(!source || !dest)
 		{
@@ -596,7 +596,12 @@
 
 		bool scaling = (sRect.x1 - sRect.x0 != dRect.x1 - dRect.x0) || (sRect.y1 - sRect.y0 != dRect.y1 - dRect.y0);
 		bool equalFormats = source->getInternalFormat() == dest->getInternalFormat();
-		bool depthStencil = egl::Image::isDepth(source->getInternalFormat()) || egl::Image::isStencil(source->getInternalFormat());
+		bool hasQuadLayout = Surface::hasQuadLayout(source->getInternalFormat()) || Surface::hasQuadLayout(dest->getInternalFormat());
+		bool fullCopy = (sRect.x0 == 0) && (sRect.y0 == 0) && (dRect.x0 == 0) && (dRect.y0 == 0) &&
+		                (sRect.x1 == sWidth) && (sRect.y1 == sHeight) && (dRect.x1 == dWidth) && (dRect.y0 == dHeight);
+		bool isDepth = (flags & Device::DEPTH_BUFFER) && egl::Image::isDepth(source->getInternalFormat());
+		bool isStencil = (flags & Device::STENCIL_BUFFER) && (egl::Image::isDepth(source->getInternalFormat()) || egl::Image::isStencil(source->getInternalFormat()));
+		bool isColor = (flags & Device::COLOR_BUFFER) == Device::COLOR_BUFFER;
 		bool alpha0xFF = false;
 
 		if((source->getInternalFormat() == FORMAT_A8R8G8B8 && dest->getInternalFormat() == FORMAT_X8R8G8B8) ||
@@ -606,31 +611,31 @@
 			alpha0xFF = true;
 		}
 
-		if(depthStencil)   // Copy entirely, internally   // FIXME: Check
+		if((isDepth || isStencil) && !scaling && equalFormats && (!hasQuadLayout || fullCopy))
 		{
-			if(source->hasDepth())
+			if(source->hasDepth() && isDepth)
 			{
-				sw::byte *sourceBuffer = (sw::byte*)source->lockInternal(0, 0, sourceRect->slice, LOCK_READONLY, PUBLIC);
-				sw::byte *destBuffer = (sw::byte*)dest->lockInternal(0, 0, destRect->slice, LOCK_DISCARD, PUBLIC);
+				sw::byte *sourceBuffer = (sw::byte*)source->lockInternal(sRect.x0, sRect.y0, 0, LOCK_READONLY, PUBLIC);
+				sw::byte *destBuffer = (sw::byte*)dest->lockInternal(dRect.x0, dRect.y0, 0, LOCK_DISCARD, PUBLIC);
 
-				copyBuffer(sourceBuffer, destBuffer, source->getWidth(), source->getHeight(), source->getInternalPitchB(), dest->getInternalPitchB(), egl::Image::bytes(source->getInternalFormat()), flipX, flipY);
+				copyBuffer(sourceBuffer, destBuffer, dRect.width(), dRect.height(), source->getInternalPitchB(), dest->getInternalPitchB(), egl::Image::bytes(source->getInternalFormat()), flipX, flipY);
 
 				source->unlockInternal();
 				dest->unlockInternal();
 			}
 
-			if(source->hasStencil())
+			if(source->hasStencil() && isStencil)
 			{
-				sw::byte *sourceBuffer = (sw::byte*)source->lockStencil(0, 0, 0, PUBLIC);
-				sw::byte *destBuffer = (sw::byte*)dest->lockStencil(0, 0, 0, PUBLIC);
+				sw::byte *sourceBuffer = (sw::byte*)source->lockStencil(sRect.x0, sRect.y0, 0, PUBLIC);
+				sw::byte *destBuffer = (sw::byte*)dest->lockStencil(dRect.x0, dRect.y0, 0, PUBLIC);
 
-				copyBuffer(sourceBuffer, destBuffer, source->getWidth(), source->getHeight(), source->getInternalPitchB(), dest->getInternalPitchB(), egl::Image::bytes(source->getInternalFormat()), flipX, flipY);
+				copyBuffer(sourceBuffer, destBuffer, source->getWidth(), source->getHeight(), source->getStencilPitchB(), dest->getStencilPitchB(), egl::Image::bytes(source->getStencilFormat()), flipX, flipY);
 
 				source->unlockStencil();
 				dest->unlockStencil();
 			}
 		}
-		else if(!scaling && equalFormats)
+		else if((flags & Device::COLOR_BUFFER) && !scaling && equalFormats && (!hasQuadLayout || fullCopy))
 		{
 			unsigned char *sourceBytes = (unsigned char*)source->lockInternal(sRect.x0, sRect.y0, sourceRect->slice, LOCK_READONLY, PUBLIC);
 			unsigned char *destBytes = (unsigned char*)dest->lockInternal(dRect.x0, dRect.y0, destRect->slice, LOCK_READWRITE, PUBLIC);
@@ -656,7 +661,7 @@
 			source->unlockInternal();
 			dest->unlockInternal();
 		}
-		else
+		else if(isColor || isDepth || isStencil)
 		{
 			if(flipX)
 			{
@@ -666,7 +671,11 @@
 			{
 				swap(dRect.y0, dRect.y1);
 			}
-			blit(source, sRect, dest, dRect, scaling && filter);
+			blit(source, sRect, dest, dRect, scaling && (flags & Device::USE_FILTER), isStencil);
+		}
+		else
+		{
+			UNREACHABLE(false);
 		}
 
 		return true;
diff --git a/src/OpenGL/libGLESv2/Device.hpp b/src/OpenGL/libGLESv2/Device.hpp
index 705597b..13a10de 100644
--- a/src/OpenGL/libGLESv2/Device.hpp
+++ b/src/OpenGL/libGLESv2/Device.hpp
@@ -39,6 +39,15 @@
 	class Device : public sw::Renderer
 	{
 	public:
+		enum : unsigned char
+		{
+			USE_FILTER = 0x01,
+			COLOR_BUFFER = 0x02,
+			DEPTH_BUFFER = 0x04,
+			STENCIL_BUFFER = 0x08,
+			ALL_BUFFERS = COLOR_BUFFER | DEPTH_BUFFER | STENCIL_BUFFER,
+		};
+
 		explicit Device(sw::Context *context);
 
 		virtual ~Device();
@@ -64,7 +73,7 @@
 		void setVertexShaderConstantF(unsigned int startRegister, const float *constantData, unsigned int count);
 		void setViewport(const Viewport &viewport);
 
-		bool stretchRect(sw::Surface *sourceSurface, const sw::SliceRect *sourceRect, sw::Surface *destSurface, const sw::SliceRect *destRect, bool filter);
+		bool stretchRect(sw::Surface *sourceSurface, const sw::SliceRect *sourceRect, sw::Surface *destSurface, const sw::SliceRect *destRect, unsigned char flags);
 		bool stretchCube(sw::Surface *sourceSurface, sw::Surface *destSurface);
 		void finish();
 
diff --git a/src/OpenGL/libGLESv2/Texture.cpp b/src/OpenGL/libGLESv2/Texture.cpp
index 50ab54b..deb8ed7 100644
--- a/src/OpenGL/libGLESv2/Texture.cpp
+++ b/src/OpenGL/libGLESv2/Texture.cpp
@@ -480,7 +480,7 @@
 	Device *device = getDevice();
 
 	sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), zoffset);
-	bool success = device->stretchRect(source, &sourceRect, dest, &destRect, false);
+	bool success = device->stretchRect(source, &sourceRect, dest, &destRect, Device::ALL_BUFFERS);
 
 	if(!success)
 	{
@@ -926,7 +926,7 @@
 			return error(GL_OUT_OF_MEMORY);
 		}
 
-		getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);
+		getDevice()->stretchRect(image[i - 1], 0, image[i], 0, Device::ALL_BUFFERS | Device::USE_FILTER);
 	}
 }
 
@@ -1415,7 +1415,7 @@
 				return error(GL_OUT_OF_MEMORY);
 			}
 
-			getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true);
+			getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, Device::ALL_BUFFERS | Device::USE_FILTER);
 		}
 	}
 }
@@ -2001,7 +2001,7 @@
 		{
 			sw::SliceRect srcRect(0, 0, srcw, srch, z);
 			sw::SliceRect dstRect(0, 0, w, h, z);
-			getDevice()->stretchRect(image[i - 1], &srcRect, image[i], &dstRect, true);
+			getDevice()->stretchRect(image[i - 1], &srcRect, image[i], &dstRect, Device::ALL_BUFFERS | Device::USE_FILTER);
 		}
 	}
 }
diff --git a/src/OpenGL/libGLESv2/libGLESv2.cpp b/src/OpenGL/libGLESv2/libGLESv2.cpp
index 2b7361d..447d800 100644
--- a/src/OpenGL/libGLESv2/libGLESv2.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv2.cpp
@@ -6236,7 +6236,7 @@
 	}
 }
 
-void BlitFramebufferNV(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+static void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter, bool allowPartialDepthStencilBlit)
 {
 	TRACE("(GLint srcX0 = %d, GLint srcY0 = %d, GLint srcX1 = %d, GLint srcY1 = %d, "
 	      "GLint dstX0 = %d, GLint dstY0 = %d, GLint dstX1 = %d, GLint dstY1 = %d, "
@@ -6266,10 +6266,15 @@
 			return error(GL_INVALID_OPERATION);
 		}
 
-		context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, false);
+		context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, false, allowPartialDepthStencilBlit);
 	}
 }
 
+void BlitFramebufferNV(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+{
+	BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter, true);
+}
+
 void BlitFramebufferANGLE(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                           GLbitfield mask, GLenum filter)
 {
@@ -6279,7 +6284,7 @@
 		return error(GL_INVALID_OPERATION);
 	}
 
-	glBlitFramebufferNV(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+	BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter, false);
 }
 
 void TexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth,
diff --git a/src/OpenGL/libGLESv2/libGLESv3.cpp b/src/OpenGL/libGLESv2/libGLESv3.cpp
index f50a65c..4c86895 100644
--- a/src/OpenGL/libGLESv2/libGLESv3.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv3.cpp
@@ -1447,7 +1447,7 @@
 			return error(GL_INVALID_OPERATION);
 		}
 
-		context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter == GL_LINEAR);
+		context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter == GL_LINEAR, true);
 	}
 }