glClearBuffer implementation

Implemented glClearBuffer* functions.
Right now, all colors are represented
as floats internally, but should that
change, and integer types be supported,
we'd be able to remove the casts to
float here.

Change-Id: Ie785858d77ce7ac03b78fca9e43ac922c15e76e0
Reviewed-on: https://swiftshader-review.googlesource.com/3004
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 a66da83..3c0aaea 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -703,6 +703,14 @@
     }

 }

 

+unsigned int Context::getColorMask() const

+{

+	return (mState.colorMaskRed ? 0x1 : 0) |

+	       (mState.colorMaskGreen ? 0x2 : 0) |

+	       (mState.colorMaskBlue ? 0x4 : 0) |

+	       (mState.colorMaskAlpha ? 0x8 : 0);

+}

+

 void Context::setDepthMask(bool mask)

 {

     if(mState.depthMask != mask)

@@ -2530,6 +2538,34 @@
     return true;

 }

 

+void Context::applyScissor(int width, int height)

+{

+	if(mState.scissorTest)

+	{

+		sw::Rect scissor = { mState.scissorX, mState.scissorY, mState.scissorX + mState.scissorWidth, mState.scissorY + mState.scissorHeight };

+		scissor.clip(0, 0, width, height);

+

+		device->setScissorRect(scissor);

+		device->setScissorEnable(true);

+	}

+	else

+	{

+		device->setScissorEnable(false);

+	}

+}

+

+egl::Image *Context::getScissoredImage(GLint drawbuffer, int &x0, int &y0, int &width, int &height, bool depthStencil)

+{

+	Framebuffer* framebuffer = getFramebuffer(drawbuffer);

+	egl::Image* image = depthStencil ? framebuffer->getDepthStencil() : framebuffer->getRenderTarget(0);

+

+	applyScissor(image->getWidth(), image->getHeight());

+

+	device->getScissoredRegion(image, x0, y0, width, height);

+

+	return image;

+}

+

 // Applies the render target surface, depth stencil surface, viewport rectangle and scissor rectangle

 bool Context::applyRenderTarget()

 {

@@ -2562,18 +2598,7 @@
 

     device->setViewport(viewport);

 

-    if(mState.scissorTest)

-    {

-		sw::Rect scissor = {mState.scissorX, mState.scissorY, mState.scissorX + mState.scissorWidth, mState.scissorY + mState.scissorHeight};

-		scissor.clip(0, 0, width, height);

-        

-		device->setScissorRect(scissor);

-        device->setScissorEnable(true);

-    }

-    else

-    {

-        device->setScissorEnable(false);

-    }

+	applyScissor(width, height);

 

 	Program *program = getCurrentProgram();

 

@@ -3368,22 +3393,16 @@
         return;

     }

 	

-	unsigned int color = (unorm<8>(mState.colorClearValue.alpha) << 24) |

-                         (unorm<8>(mState.colorClearValue.red) << 16) |

-                         (unorm<8>(mState.colorClearValue.green) << 8) | 

-                         (unorm<8>(mState.colorClearValue.blue) << 0);

-    float depth = clamp01(mState.depthClearValue);

-    int stencil = mState.stencilClearValue & 0x000000FF;

-

 	if(mask & GL_COLOR_BUFFER_BIT)

 	{

-		unsigned int rgbaMask = (mState.colorMaskRed ? 0x1 : 0) |

-		                        (mState.colorMaskGreen ? 0x2 : 0) | 

-		                        (mState.colorMaskBlue ? 0x4 : 0) |

-		                        (mState.colorMaskAlpha ? 0x8 : 0);

+		unsigned int rgbaMask = getColorMask();

 

 		if(rgbaMask != 0)

 		{

+			unsigned int color = (unorm<8>(mState.colorClearValue.alpha) << 24) |

+			                     (unorm<8>(mState.colorClearValue.red) << 16) |

+			                     (unorm<8>(mState.colorClearValue.green) << 8) |

+			                     (unorm<8>(mState.colorClearValue.blue) << 0);

 			device->clearColor(color, rgbaMask);

 		}

 	}

@@ -3392,6 +3411,7 @@
 	{

 		if(mState.depthMask != 0)

 		{

+			float depth = clamp01(mState.depthClearValue);

 			device->clearDepth(depth);

 		}

 	}

@@ -3400,11 +3420,103 @@
 	{

 		if(mState.stencilWritemask != 0)

 		{

+			int stencil = mState.stencilClearValue & 0x000000FF;

 			device->clearStencil(stencil, mState.stencilWritemask);

 		}

 	}

 }

 

+void Context::clearColorBuffer(GLint drawbuffer, const GLint *value)

+{

+	unsigned int rgbaMask = getColorMask();

+	if(device && rgbaMask)

+	{

+		int x0(0), y0(0), width(0), height(0);

+		egl::Image* image = getScissoredImage(drawbuffer, x0, y0, width, height, false);

+

+		unsigned int color = (value[0] < 0 ? 0 : (value[0] & 0x7F800000) << 1) |

+		                     (value[1] < 0 ? 0 : (value[1] & 0x7F800000) >> 7) |

+		                     (value[2] < 0 ? 0 : (value[2] & 0x7F800000) >> 15) |

+		                     (value[3] < 0 ? 0 : (value[3] & 0x7F800000) >> 23);

+		image->clearColorBuffer(color, rgbaMask, x0, y0, width, height);

+	}

+}

+

+void Context::clearColorBuffer(GLint drawbuffer, const GLuint *value)

+{

+	unsigned int rgbaMask = getColorMask();

+	if(device && rgbaMask)

+	{

+		int x0(0), y0(0), width(0), height(0);

+		egl::Image* image = getScissoredImage(drawbuffer, x0, y0, width, height, false);

+

+		unsigned int color = (value[0] & 0xFF000000) >> 0 |

+		                     (value[1] & 0xFF000000) >> 8 |

+		                     (value[2] & 0xFF000000) >> 16 |

+		                     (value[3] & 0xFF000000) >> 24;

+		image->clearColorBuffer(color, rgbaMask, x0, y0, width, height);

+	}

+}

+

+void Context::clearColorBuffer(GLint drawbuffer, const GLfloat *value)

+{

+	unsigned int rgbaMask = getColorMask();

+	if(device && rgbaMask)

+	{

+		int x0(0), y0(0), width(0), height(0);

+		egl::Image* image = getScissoredImage(drawbuffer, x0, y0, width, height, false);

+

+		unsigned int color = (unorm<8>(value[0]) << 24) |

+		                     (unorm<8>(value[1]) << 16) |

+		                     (unorm<8>(value[2]) << 8) |

+		                     (unorm<8>(value[3]) << 0);

+		image->clearColorBuffer(color, rgbaMask, x0, y0, width, height);

+	}

+}

+

+void Context::clearDepthBuffer(GLint drawbuffer, const GLfloat *value)

+{

+	if(device && mState.depthMask)

+	{

+		int x0(0), y0(0), width(0), height(0);

+		egl::Image* image = getScissoredImage(drawbuffer, x0, y0, width, height, true);

+

+		float depth = clamp01(value[0]);

+		image->clearDepthBuffer(depth, x0, y0, width, height);

+	}

+}

+

+void Context::clearStencilBuffer(GLint drawbuffer, const GLint *value)

+{

+	if(device && mState.stencilWritemask)

+	{

+		int x0(0), y0(0), width(0), height(0);

+		egl::Image* image = getScissoredImage(drawbuffer, x0, y0, width, height, true);

+

+		unsigned char stencil = value[0] < 0 ? 0 : static_cast<unsigned char>(value[0] & 0x000000FF);

+		image->clearStencilBuffer(stencil, static_cast<unsigned char>(mState.stencilWritemask), x0, y0, width, height);

+	}

+}

+

+void Context::clearDepthStencilBuffer(GLint drawbuffer, GLfloat depth, GLint stencil)

+{

+	if(device && (mState.depthMask || mState.stencilWritemask))

+	{

+		int x0(0), y0(0), width(0), height(0);

+		egl::Image* image = getScissoredImage(drawbuffer, x0, y0, width, height, true);

+

+		if(mState.stencilWritemask)

+		{

+			image->clearStencilBuffer(static_cast<unsigned char>(stencil & 0x000000FF), static_cast<unsigned char>(mState.stencilWritemask), x0, y0, width, height);

+		}

+

+		if(mState.depthMask)

+		{

+			image->clearDepthBuffer(clamp01(depth), x0, y0, width, height);

+		}

+	}

+}

+

 void Context::drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount)

 {

     if(!mState.currentProgram)

diff --git a/src/OpenGL/libGLESv2/Context.h b/src/OpenGL/libGLESv2/Context.h
index f7795e3..fe881c1 100644
--- a/src/OpenGL/libGLESv2/Context.h
+++ b/src/OpenGL/libGLESv2/Context.h
@@ -425,6 +425,7 @@
     void setScissorParams(GLint x, GLint y, GLsizei width, GLsizei height);

 

     void setColorMask(bool red, bool green, bool blue, bool alpha);

+    unsigned int getColorMask() const;

     void setDepthMask(bool mask);

 

     void setActiveSampler(unsigned int active);

@@ -579,6 +580,12 @@
 

     void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei *bufSize, void* pixels);

     void clear(GLbitfield mask);

+    void clearColorBuffer(GLint drawbuffer, const GLint *value);

+    void clearColorBuffer(GLint drawbuffer, const GLuint *value);

+    void clearColorBuffer(GLint drawbuffer, const GLfloat *value);

+    void clearDepthBuffer(GLint drawbuffer, const GLfloat *value);

+    void clearStencilBuffer(GLint drawbuffer, const GLint *value);

+    void clearDepthStencilBuffer(GLint drawbuffer, GLfloat depth, GLint stencil);

     void drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount = 1);

     void drawElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLsizei instanceCount = 1);

     void finish();

@@ -609,6 +616,8 @@
 private:

 	virtual ~Context();

 

+	egl::Image *getScissoredImage(GLint drawbuffer, int &x0, int &y0, int &width, int &height, bool depthStencil);

+	void applyScissor(int width, int height);

     bool applyRenderTarget();

     void applyState(GLenum drawMode);

 	GLenum applyVertexBuffer(GLint base, GLint first, GLsizei count, GLsizei instanceId);

diff --git a/src/OpenGL/libGLESv2/Device.cpp b/src/OpenGL/libGLESv2/Device.cpp
index 5eabab1..cb7ef48 100644
--- a/src/OpenGL/libGLESv2/Device.cpp
+++ b/src/OpenGL/libGLESv2/Device.cpp
@@ -157,17 +157,12 @@
 		delete context;

 	}

 

-	void Device::clearColor(unsigned int color, unsigned int rgbaMask)

+	void Device::getScissoredRegion(egl::Image *sourceSurface, int &x0, int &y0, int& width, int& height) const

 	{

-		if(!renderTarget)

-		{

-			return;

-		}

-

-		int x0 = 0;

-		int y0 = 0;

-		int width = renderTarget->getExternalWidth();

-		int height = renderTarget->getExternalHeight();

+		x0 = 0;

+		y0 = 0;

+		width = sourceSurface->getExternalWidth();

+		height = sourceSurface->getExternalHeight();

 

 		if(scissorEnable)   // Clamp against scissor rectangle

 		{

@@ -176,6 +171,17 @@
 			if(width > scissorRect.x1 - scissorRect.x0) width = scissorRect.x1 - scissorRect.x0;

 			if(height > scissorRect.y1 - scissorRect.y0) height = scissorRect.y1 - scissorRect.y0;

 		}

+	}

+

+	void Device::clearColor(unsigned int color, unsigned int rgbaMask)

+	{

+		if(!renderTarget)

+		{

+			return;

+		}

+

+		int x0(0), y0(0), width(0), height(0);

+		getScissoredRegion(renderTarget, x0, y0, width, height);

 

 		renderTarget->clearColorBuffer(color, rgbaMask, x0, y0, width, height);

 	}

@@ -190,18 +196,8 @@
 		if(z > 1) z = 1;

 		if(z < 0) z = 0;

 

-		int x0 = 0;

-		int y0 = 0;

-		int width = depthStencil->getExternalWidth();

-		int height = depthStencil->getExternalHeight();

-

-		if(scissorEnable)   // Clamp against scissor rectangle

-		{

-			if(x0 < scissorRect.x0) x0 = scissorRect.x0;

-			if(y0 < scissorRect.y0) y0 = scissorRect.y0;

-			if(width > scissorRect.x1 - scissorRect.x0) width = scissorRect.x1 - scissorRect.x0;

-			if(height > scissorRect.y1 - scissorRect.y0) height = scissorRect.y1 - scissorRect.y0;

-		}

+		int x0(0), y0(0), width(0), height(0);

+		getScissoredRegion(depthStencil, x0, y0, width, height);

 			

 		depthStencil->clearDepthBuffer(z, x0, y0, width, height);

 	}

@@ -213,18 +209,8 @@
 			return;

 		}

 

-		int x0 = 0;

-		int y0 = 0;

-		int width = depthStencil->getExternalWidth();

-		int height = depthStencil->getExternalHeight();

-

-		if(scissorEnable)   // Clamp against scissor rectangle

-		{

-			if(x0 < scissorRect.x0) x0 = scissorRect.x0;

-			if(y0 < scissorRect.y0) y0 = scissorRect.y0;

-			if(width > scissorRect.x1 - scissorRect.x0) width = scissorRect.x1 - scissorRect.x0;

-			if(height > scissorRect.y1 - scissorRect.y0) height = scissorRect.y1 - scissorRect.y0;

-		}

+		int x0(0), y0(0), width(0), height(0);

+		getScissoredRegion(depthStencil, x0, y0, width, height);

 

 		depthStencil->clearStencilBuffer(stencil, mask, x0, y0, width, height);

 	}

diff --git a/src/OpenGL/libGLESv2/Device.hpp b/src/OpenGL/libGLESv2/Device.hpp
index 26bc248..4cf1860 100644
--- a/src/OpenGL/libGLESv2/Device.hpp
+++ b/src/OpenGL/libGLESv2/Device.hpp
@@ -72,6 +72,8 @@
 		virtual bool stretchCube(egl::Image *sourceSurface, egl::Image *destSurface);

 		virtual void finish();

 

+		void getScissoredRegion(egl::Image *sourceSurface, int &x0, int &y0, int& width, int& height) const;

+

 	private:

 		sw::Context *const context;

 

diff --git a/src/OpenGL/libGLESv2/libGLESv3.cpp b/src/OpenGL/libGLESv2/libGLESv3.cpp
index 56d825a..3d59a1d 100644
--- a/src/OpenGL/libGLESv2/libGLESv3.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv3.cpp
@@ -2421,25 +2421,36 @@
 	TRACE("(GLenum buffer = 0x%X, GLint drawbuffer = %d, const GLint *value = %p)",

 	      buffer, drawbuffer, value);

 

-	switch(buffer)

-	{

-	case GL_COLOR:

-		if(drawbuffer > es2::IMPLEMENTATION_MAX_DRAW_BUFFERS)

-		{

-			return error(GL_INVALID_VALUE);

-		}

-		break;

-	case GL_STENCIL:

-		if(drawbuffer != 0)

-		{

-			return error(GL_INVALID_VALUE);

-		}

-		break;

-	default:

-		return error(GL_INVALID_ENUM);

-	}

+	es2::Context *context = es2::getContext();

 

-	UNIMPLEMENTED();

+	if(context)

+	{

+		switch(buffer)

+		{

+		case GL_COLOR:

+			if(drawbuffer < 0 || drawbuffer >= es2::IMPLEMENTATION_MAX_DRAW_BUFFERS)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			else

+			{

+				context->clearColorBuffer(drawbuffer, value);

+			}

+			break;

+		case GL_STENCIL:

+			if(drawbuffer != 0)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			else

+			{

+				context->clearStencilBuffer(drawbuffer, value);

+			}

+			break;

+		default:

+			return error(GL_INVALID_ENUM);

+		}

+	}

 }

 

 GL_APICALL void GL_APIENTRY glClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint *value)

@@ -2447,19 +2458,26 @@
 	TRACE("(GLenum buffer = 0x%X, GLint drawbuffer = %d, const GLuint *value = %p)",

 	      buffer, drawbuffer, value);

 

-	switch(buffer)

-	{

-	case GL_COLOR:

-		if(drawbuffer > es2::IMPLEMENTATION_MAX_DRAW_BUFFERS)

-		{

-			return error(GL_INVALID_VALUE);

-		}

-		break;

-	default:

-		return error(GL_INVALID_ENUM);

-	}

+	es2::Context *context = es2::getContext();

 

-	UNIMPLEMENTED();

+	if(context)

+	{

+		switch(buffer)

+		{

+		case GL_COLOR:

+			if(drawbuffer < 0 || drawbuffer >= es2::IMPLEMENTATION_MAX_DRAW_BUFFERS)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			else

+			{

+				context->clearColorBuffer(drawbuffer, value);

+			}

+			break;

+		default:

+			return error(GL_INVALID_ENUM);

+		}

+	}

 }

 

 GL_APICALL void GL_APIENTRY glClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat *value)

@@ -2467,25 +2485,36 @@
 	TRACE("(GLenum buffer = 0x%X, GLint drawbuffer = %d, const GLfloat *value = %p)",

 	      buffer, drawbuffer, value);

 

-	switch(buffer)

-	{

-	case GL_COLOR:

-		if(drawbuffer > es2::IMPLEMENTATION_MAX_DRAW_BUFFERS)

-		{

-			return error(GL_INVALID_VALUE);

-		}

-		break;

-	case GL_DEPTH:

-		if(drawbuffer != 0)

-		{

-			return error(GL_INVALID_VALUE);

-		}

-		break;

-	default:

-		return error(GL_INVALID_ENUM);

-	}

+	es2::Context *context = es2::getContext();

 

-	UNIMPLEMENTED();

+	if(context)

+	{

+		switch(buffer)

+		{

+		case GL_COLOR:

+			if(drawbuffer < 0 || drawbuffer >= es2::IMPLEMENTATION_MAX_DRAW_BUFFERS)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			else

+			{

+				context->clearColorBuffer(drawbuffer, value);

+			}

+			break;

+		case GL_DEPTH:

+			if(drawbuffer != 0)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			else

+			{

+				context->clearDepthBuffer(drawbuffer, value);

+			}

+			break;

+		default:

+			return error(GL_INVALID_ENUM);

+		}

+	}

 }

 

 GL_APICALL void GL_APIENTRY glClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)

@@ -2493,19 +2522,26 @@
 	TRACE("(GLenum buffer = 0x%X, GLint drawbuffer = %d, GLfloat depth = %f, GLint stencil = %d)",

 	      buffer, drawbuffer, depth, stencil);

 

-	switch(buffer)

-	{

-	case GL_DEPTH_STENCIL:

-		if(drawbuffer != 0)

-		{

-			return error(GL_INVALID_VALUE);

-		}

-		break;

-	default:

-		return error(GL_INVALID_ENUM);

-	}

+	es2::Context *context = es2::getContext();

 

-	UNIMPLEMENTED();

+	if(context)

+	{

+		switch(buffer)

+		{

+		case GL_DEPTH_STENCIL:

+			if(drawbuffer != 0)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			else

+			{

+				context->clearDepthStencilBuffer(drawbuffer, depth, stencil);

+			}

+			break;

+		default:

+			return error(GL_INVALID_ENUM);

+		}

+	}

 }

 

 GL_APICALL const GLubyte *GL_APIENTRY glGetStringi(GLenum name, GLuint index)