Transform feedback buffer assignment

This cl adds a utility function that assigns the correct buffer
for each linked transform feedback variable. All the information
about location and size is sent to the VertexProcessor for use
during rendering.

Change-Id: I942805250804f56805de1fc117024c20976e83a0
Reviewed-on: https://swiftshader-review.googlesource.com/5174
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/OpenGL/libGLESv2/Program.cpp b/src/OpenGL/libGLESv2/Program.cpp
index 9bfee2b..6a790f4 100644
--- a/src/OpenGL/libGLESv2/Program.cpp
+++ b/src/OpenGL/libGLESv2/Program.cpp
@@ -17,6 +17,7 @@
 #include "main.h"

 #include "Buffer.h"

 #include "Shader.h"

+#include "TransformFeedback.h"

 #include "utilities.h"

 #include "common/debug.h"

 #include "Shader/PixelShader.hpp"

@@ -1182,6 +1183,90 @@
 		}

 	}

 

+	void Program::applyTransformFeedback(TransformFeedback* transformFeedback)

+	{

+		// Make sure the flags will fit in a 64 bit unsigned int variable

+		ASSERT(sw::max<int>(MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, sw::MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS) <= 64);

+

+		BufferBinding* transformFeedbackBuffers = (transformFeedback && transformFeedback->isActive() && !transformFeedback->isPaused()) ? transformFeedback->getBuffers() : nullptr;

+

+		uint64_t enableTransformFeedback = 0;

+		if(!transformFeedbackBuffers)

+		{

+			for(unsigned int index = 0; index < sw::MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS; ++index)

+			{

+				device->VertexProcessor::setTransformFeedbackBuffer(index, nullptr, 0, 0, 0, 0, 0);

+			}

+			device->VertexProcessor::enableTransformFeedback(enableTransformFeedback);

+			return;

+		}

+

+		unsigned int maxVaryings = transformFeedbackLinkedVaryings.size();

+		switch(transformFeedbackBufferMode)

+		{

+		case GL_SEPARATE_ATTRIBS:

+		{

+			maxVaryings = sw::min(maxVaryings, (unsigned int)MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS);

+			// Attribs go to separate buffers

+			for(unsigned int index = 0; index < maxVaryings; ++index)

+			{

+				int size = transformFeedbackLinkedVaryings[index].size;

+				int rowCount = VariableRowCount(transformFeedbackLinkedVaryings[index].type);

+				int colCount = VariableColumnCount(transformFeedbackLinkedVaryings[index].type);

+				int nbRegs = rowCount > 1 ? colCount * size : size;

+				int nbComponentsPerReg = rowCount > 1 ? rowCount : colCount;

+				int componentStride = rowCount * colCount * size;

+				int baseOffset = transformFeedback->vertexOffset() * componentStride * sizeof(float);

+				device->VertexProcessor::setTransformFeedbackBuffer(index,

+					transformFeedbackBuffers[index].get()->getResource(),

+					transformFeedbackBuffers[index].getOffset() + baseOffset,

+					transformFeedbackLinkedVaryings[index].reg * 4 + transformFeedbackLinkedVaryings[index].col,

+					nbRegs, nbComponentsPerReg, componentStride);

+				enableTransformFeedback |= 1ULL << index;

+			}

+		}

+			break;

+		case GL_INTERLEAVED_ATTRIBS:

+		{

+			// OpenGL ES 3.0.4 spec, section 2.15.2:

+			// In INTERLEAVED_ATTRIBS mode, the values of one or more output variables

+			// written by a vertex shader are written, interleaved, into the buffer object

+			// bound to the first transform feedback binding point (index = 0).

+			sw::Resource* resource = transformFeedbackBuffers[0].get()->getResource();

+			int componentStride = totalLinkedVaryingsComponents;

+			int baseOffset = transformFeedbackBuffers[0].getOffset() + (transformFeedback->vertexOffset() * componentStride * sizeof(float));

+			maxVaryings = sw::min(maxVaryings, (unsigned int)sw::MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS);

+			size_t totalComponents = 0;

+			for(unsigned int index = 0; index < maxVaryings; ++index)

+			{

+				int size = transformFeedbackLinkedVaryings[index].size;

+				int rowCount = VariableRowCount(transformFeedbackLinkedVaryings[index].type);

+				int colCount = VariableColumnCount(transformFeedbackLinkedVaryings[index].type);

+				int nbRegs = rowCount > 1 ? colCount * size : size;

+				int nbComponentsPerReg = rowCount > 1 ? rowCount : colCount;

+				device->VertexProcessor::setTransformFeedbackBuffer(index, resource,

+					baseOffset + (totalComponents * sizeof(float)),

+					transformFeedbackLinkedVaryings[index].reg * 4 + transformFeedbackLinkedVaryings[index].col,

+					nbRegs, nbComponentsPerReg, componentStride);

+				totalComponents += rowCount * colCount * size;

+				enableTransformFeedback |= 1ULL << index;

+			}

+		}

+			break;

+		default:

+			UNREACHABLE(transformFeedbackBufferMode);

+			break;

+		}

+

+		// Unset all other transform feedback buffers

+		for(unsigned int index = maxVaryings; index < sw::MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS; ++index)

+		{

+			device->VertexProcessor::setTransformFeedbackBuffer(index, nullptr, 0, 0, 0, 0, 0);

+		}

+

+		device->VertexProcessor::enableTransformFeedback(enableTransformFeedback);

+	}

+

 	bool Program::linkVaryings()

 	{

 		for(glsl::VaryingList::iterator input = fragmentShader->varyings.begin(); input != fragmentShader->varyings.end(); ++input)

diff --git a/src/OpenGL/libGLESv2/Program.h b/src/OpenGL/libGLESv2/Program.h
index 1ff8f05..2d08d89 100644
--- a/src/OpenGL/libGLESv2/Program.h
+++ b/src/OpenGL/libGLESv2/Program.h
@@ -171,6 +171,7 @@
 		void dirtyAllUniforms();

 		void applyUniforms();

 		void applyUniformBuffers(BufferBinding* uniformBuffers);

+		void applyTransformFeedback(TransformFeedback* transformFeedback);

 

 		void link();

 		bool isLinked() const;

diff --git a/src/OpenGL/libGLESv2/TransformFeedback.cpp b/src/OpenGL/libGLESv2/TransformFeedback.cpp
index 3d5e2b3..44df63e 100644
--- a/src/OpenGL/libGLESv2/TransformFeedback.cpp
+++ b/src/OpenGL/libGLESv2/TransformFeedback.cpp
@@ -16,7 +16,7 @@
 namespace es2

 {

 

-TransformFeedback::TransformFeedback(GLuint name) : NamedObject(name), mActive(false), mPaused(false)

+TransformFeedback::TransformFeedback(GLuint name) : NamedObject(name), mActive(false), mPaused(false), mVertexOffset(0)

 {

 	mGenericBuffer = NULL;

 }

@@ -60,6 +60,19 @@
 	return mBuffer[index].getSize();

 }

 

+void TransformFeedback::addVertexOffset(int count)

+{

+	if(isActive() && !isPaused())

+	{

+		mVertexOffset += count;

+	}

+}

+

+int TransformFeedback::vertexOffset() const

+{

+	return mVertexOffset;

+}

+

 bool TransformFeedback::isActive() const

 {

 	return mActive;

@@ -84,6 +97,7 @@
 {

 	mActive = false;

 	mPaused = false;

+	mVertexOffset = 0;

 }

 

 void TransformFeedback::setPaused(bool paused)

diff --git a/src/OpenGL/libGLESv2/TransformFeedback.h b/src/OpenGL/libGLESv2/TransformFeedback.h
index 4871ee1..0bc11b6 100644
--- a/src/OpenGL/libGLESv2/TransformFeedback.h
+++ b/src/OpenGL/libGLESv2/TransformFeedback.h
@@ -27,10 +27,11 @@
 class TransformFeedback : public gl::NamedObject

 {

 public:

-	// FIXME: Change this when implementing transform feedback

 	TransformFeedback(GLuint name);

 	~TransformFeedback();

 

+	BufferBinding* getBuffers() { return mBuffer; }

+

 	Buffer* getGenericBuffer() const;

 	Buffer* getBuffer(GLuint index) const;

 	GLuint getGenericBufferName() const;

@@ -40,6 +41,7 @@
 	bool isActive() const;

 	bool isPaused() const;

 	GLenum primitiveMode() const;

+	int vertexOffset() const;

 

 	void setGenericBuffer(Buffer* buffer);

 	void setBuffer(GLuint index, Buffer* buffer);

@@ -48,6 +50,7 @@
 	void begin(GLenum primitiveMode);

 	void end();

 	void setPaused(bool paused);

+	void addVertexOffset(int count);

 

 private:

 	gl::BindingPointer<Buffer> mGenericBuffer;

@@ -56,6 +59,7 @@
 	bool mActive;

 	bool mPaused;

 	GLenum mPrimitiveMode;

+	int mVertexOffset;

 };

 

 }