vrld avatar vrld committed acc815b Merge

Merge default branch

Comments (0)

Files changed (8)

src/modules/audio/wrap_Source.cpp

 	{ "setVolumeLimits", w_Source_setVolumeLimits },
 	{ "getVolumeLimits", w_Source_getVolumeLimits },
 	{ "setDistance", w_Source_setDistance },
-	{ "getDistance", w_Source_setDistance },
+	{ "getDistance", w_Source_getDistance },
 	{ "setRolloff", w_Source_setRolloff},
 	{ "getRolloff", w_Source_getRolloff},
 

src/modules/graphics/opengl/Graphics.cpp

 void Graphics::push()
 {
 	if (userMatrices == matrixLimit)
-		throw Exception("Maximum stack depth reached.");
+		throw Exception("Maximum stack depth reached. (More pushes than pops?)");
 	glPushMatrix();
 	++userMatrices;
 }

src/modules/graphics/opengl/SpriteBatch.cpp

 	, array_buf(0)
 	, element_buf(0)
 {
-	image->retain();
-
 	GLenum gl_usage;
 
 	switch (usage)
 		break;
 	}
 
-	int vertex_size = sizeof(vertex) * 4 * size;
-	int element_size = sizeof(GLushort) * 6 * size;
+	const size_t vertex_size = sizeof(vertex) * 4 * size;
 
-	array_buf = VertexBuffer::Create(vertex_size, GL_ARRAY_BUFFER, gl_usage);
-	element_buf = VertexBuffer::Create(element_size, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
+	try
+	{
+		array_buf = VertexBuffer::Create(vertex_size, GL_ARRAY_BUFFER, gl_usage);
+		element_buf = new VertexIndex(size);
+	}
+	catch (love::Exception &)
+	{
+		delete array_buf;
+		delete element_buf;
+		throw;
+	}
+	catch (std::bad_alloc &)
+	{
+		delete array_buf;
+		delete element_buf;
+		throw love::Exception("Out of memory.");
+	}
 
-	// Fill element buffer.
-	{
-		VertexBuffer::Bind bind(*element_buf);
-		VertexBuffer::Mapper mapper(*element_buf);
-
-		GLushort *indices = static_cast<GLushort *>(mapper.get());
-
-		if (indices)
-		{
-			for (int i = 0; i < size; ++i)
-			{
-				indices[i*6+0] = 0+(i*4);
-				indices[i*6+1] = 1+(i*4);
-				indices[i*6+2] = 2+(i*4);
-
-				indices[i*6+3] = 0+(i*4);
-				indices[i*6+4] = 2+(i*4);
-				indices[i*6+5] = 3+(i*4);
-			}
-		}
-	}
+	image->retain();
 }
 
 SpriteBatch::~SpriteBatch()
 	if (!this->color)
 		this->color = new Color(color);
 	else
-		 *(this->color) = color;
+		*(this->color) = color;
 }
 
 void SpriteBatch::setColor()
 	image->bind();
 
 	VertexBuffer::Bind array_bind(*array_buf);
-	VertexBuffer::Bind element_bind(*element_buf);
+	VertexBuffer::Bind element_bind(*element_buf->getVertexBuffer());
 
 	// Apply per-sprite color, if a color is set.
 	if (color)
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), array_buf->getPointer(texel_offset));
 
-	glDrawElements(GL_TRIANGLES, next*6, GL_UNSIGNED_SHORT, element_buf->getPointer(0));
+	glDrawElements(GL_TRIANGLES, element_buf->getIndexCount(next), element_buf->getType(), element_buf->getPointer(0));
 
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	return usageHints.find(in, out);
 }
 
-bool SpriteBatch::getConstant(UsageHint in, const char  *&out)
+bool SpriteBatch::getConstant(UsageHint in, const char *&out)
 {
 	return usageHints.find(in, out);
 }

src/modules/graphics/opengl/SpriteBatch.h

 class Image;
 class Quad;
 class VertexBuffer;
+class VertexIndex;
 
 class SpriteBatch : public Drawable
 {
 	void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
 
 	static bool getConstant(const char *in, UsageHint &out);
-	static bool getConstant(UsageHint in, const char  *&out);
+	static bool getConstant(UsageHint in, const char *&out);
 
 private:
 
 	Color *color;
 
 	VertexBuffer *array_buf;
-	VertexBuffer *element_buf;
+	VertexIndex *element_buf;
+
 }; // SpriteBatch
 
 } // opengl

src/modules/graphics/opengl/VertexBuffer.cpp

 
 #include <cstdlib>
 #include <cstring>
+#include <algorithm>
 
 namespace love
 {
 VBO::VBO(size_t size, GLenum target, GLenum usage)
 	: VertexBuffer(size, target, usage)
 	, vbo(0)
-	, buffer_copy(0)
-	, mapped(0)
+	, memory_map(0)
+	, is_mapped(false)
+	, is_dirty(true)
 {
 	if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5))
 		throw love::Exception("Not supported");
 {
 	if (vbo != 0)
 		unload(false);
+
+	if (memory_map)
+		free(memory_map);
 }
 
 void *VBO::map()
 {
-	// mapping twice could result in memory leaks
-	if (mapped)
-		throw love::Exception("VBO is already mapped!");
+	if (is_mapped)
+		return memory_map;
 
-	mapped = malloc(getSize());
-	if (!mapped)
-		throw love::Exception("Out of memory (oh the humanity!)");
-	glGetBufferSubDataARB(getTarget(), 0, getSize(), mapped);
+	if (!memory_map)
+	{
+		memory_map = malloc(getSize());
+		if (!memory_map)
+			throw love::Exception("Out of memory (oh the humanity!)");
+	}
 
-	return mapped;
+	if (is_dirty)
+		glGetBufferSubDataARB(getTarget(), 0, getSize(), memory_map);
+
+	is_mapped = true;
+	is_dirty = false;
+
+	return memory_map;
 }
 
 void VBO::unmap()
 {
-	if (mapped)
-	{
-		glBufferSubDataARB(getTarget(), 0, getSize(), mapped);
-		free(mapped);
-		mapped = 0;
-	}
+	if (!is_mapped)
+		return;
+
+	// "orphan" current buffer to avoid implicit synchronisation on the gpu:
+	// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
+	glBufferDataARB(getTarget(), getSize(), NULL,       getUsage());
+	glBufferDataARB(getTarget(), getSize(), memory_map, getUsage());
+	is_mapped = false;
 }
 
 void VBO::bind()
 {
-	glBindBufferARB(getTarget(), vbo);
+	if (!is_mapped)
+		glBindBufferARB(getTarget(), vbo);
 }
 
 void VBO::unbind()
 {
-	glBindBufferARB(getTarget(), 0);
+	if (!is_mapped)
+		glBindBufferARB(getTarget(), 0);
 }
 
 void VBO::fill(size_t offset, size_t size, const void *data)
 {
-	if (mapped)
-		memcpy(static_cast<char *>(mapped) + offset, data, size);
+	if (is_mapped)
+	{
+		memcpy(static_cast<char *>(memory_map) + offset, data, size);
+	}
 	else
+	{
 		glBufferSubDataARB(getTarget(), offset, size, data);
+		is_dirty = true;
+	}
 }
 
 const void *VBO::getPointer(size_t offset) const
 	VertexBuffer::Bind bind(*this);
 
 	// Copy the old buffer only if 'restore' was requested.
-	const GLvoid *src = restore ? buffer_copy : 0;
+	const GLvoid *src = restore ? memory_map : 0;
 
 	while (GL_NO_ERROR != glGetError())
 		/* clear error messages */;
 	glBufferDataARB(getTarget(), getSize(), src, getUsage());
 	GLenum err = glGetError();
 
-	// Clean up buffer_copy, if it exists.
-	delete[] buffer_copy;
-	buffer_copy = 0;
-
 	return (GL_NO_ERROR == err);
 }
 
 void VBO::unload(bool save)
 {
-	// Clean up buffer_copy, if it exists.
-	delete[] buffer_copy;
-	buffer_copy = 0;
-
 	// Save data before unloading.
 	if (save)
 	{
 		GLint size;
 		glGetBufferParameterivARB(getTarget(), GL_BUFFER_SIZE, &size);
 
-		const char *src = static_cast<char *>(map());
-
-		if (src)
-		{
-			buffer_copy = new char[size];
-			memcpy(buffer_copy, src, size);
-			unmap();
-		}
+		map(); // saves buffer content to memory_map.
+		unmap();
 	}
 
-	glDeleteBuffers(1, &vbo);
+	glDeleteBuffersARB(1, &vbo);
 	vbo = 0;
 }
 
+
+// VertexIndex
+
+size_t VertexIndex::maxSize = 0;
+std::list<size_t> VertexIndex::sizeRefs;
+VertexBuffer *VertexIndex::element_array = NULL;
+
+VertexIndex::VertexIndex(size_t size)
+	: size(size)
+{
+	// The upper limit is the maximum of GLuint divided by six (the number
+	// of indices per size) and divided by the size of GLuint. This guarantees
+	// no overflows when calculating the array size in bytes.
+	// Memory issues will be handled by other exceptions.
+	if (size == 0 || size > ((GLuint) -1) / 6 / sizeof(GLuint))
+		throw love::Exception("Invalid size.");
+
+	addSize(size);
+}
+
+VertexIndex::~VertexIndex()
+{
+	removeSize(size);
+}
+
+size_t VertexIndex::getSize() const
+{
+	return size;
+}
+
+size_t VertexIndex::getIndexCount(size_t elements) const
+{
+	return elements * 6;
+}
+
+GLenum VertexIndex::getType(size_t s) const
+{
+	// Calculates if unsigned short is big enough to hold all the vertex indices.
+	static const GLint type_table[] = {GL_UNSIGNED_INT, GL_UNSIGNED_SHORT};
+	return type_table[int(GLushort(-1) < s * 4)];
+}
+
+VertexBuffer *VertexIndex::getVertexBuffer() const
+{
+	return element_array;
+}
+
+const void *VertexIndex::getPointer(size_t offset) const
+{
+	return element_array->getPointer(offset);
+}
+
+void VertexIndex::addSize(size_t newSize)
+{
+	if (newSize <= maxSize)
+	{
+		// Current size is bigger. Append the size to list and sort.
+		sizeRefs.push_back(newSize);
+		sizeRefs.sort();
+		return;
+	}
+
+	// Try to resize before adding it to the list because resize may throw.
+	resize(newSize);
+	sizeRefs.push_back(newSize);
+}
+
+void VertexIndex::removeSize(size_t oldSize)
+{
+	// TODO: For debugging purposes, this should check if the size was actually found.
+	sizeRefs.erase(std::find(sizeRefs.begin(), sizeRefs.end(), oldSize));
+	if (sizeRefs.size() == 0)
+	{
+		resize(0);
+		return;
+	}
+
+	if (oldSize == maxSize)
+	{
+		// Shrink if there's a smaller size.
+		size_t newSize = sizeRefs.back();
+		if (newSize < maxSize)
+			resize(newSize);
+	}
+}
+
+void VertexIndex::resize(size_t size)
+{
+	if (size == 0)
+	{
+		delete element_array;
+		element_array = NULL;
+		maxSize = 0;
+		return;
+	}
+
+	VertexBuffer *new_element_array;
+	// Depending on the size, a switch to int and more memory is needed.
+	GLenum target_type = getType(size);
+	size_t array_size = (target_type == GL_UNSIGNED_SHORT ? sizeof(GLushort) : sizeof(GLuint)) * 6 * size;
+
+	// Create may throw out-of-memory exceptions.
+	// VertexIndex will propagate the exception and keep the old VertexBuffer.
+	try
+	{
+		new_element_array = VertexBuffer::Create(array_size, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
+	}
+	catch (std::bad_alloc &)
+	{
+		throw love::Exception("Out of memory.");
+	}
+
+	// Allocation of the new VertexBuffer succeeded.
+	// The old VertexBuffer can now be deleted.
+	delete element_array;
+	element_array = new_element_array;
+	maxSize = size;
+
+	switch (target_type)
+	{
+	case GL_UNSIGNED_SHORT:
+		fill<GLushort>();
+		break;
+	case GL_UNSIGNED_INT:
+		fill<GLuint>();
+		break;
+	}
+}
+
+template <typename T>
+void VertexIndex::fill()
+{
+	VertexBuffer::Bind bind(*element_array);
+	VertexBuffer::Mapper mapper(*element_array);
+
+	T *indices = (T *) mapper.get();
+
+	for (size_t i = 0; i < maxSize; ++i)
+	{
+		indices[i*6+0] = i * 4 + 0;
+		indices[i*6+1] = i * 4 + 1;
+		indices[i*6+2] = i * 4 + 2;
+
+		indices[i*6+3] = i * 4 + 0;
+		indices[i*6+4] = i * 4 + 2;
+		indices[i*6+5] = i * 4 + 3;
+	}
+}
+
 } // opengl
 } // graphics
 } // love

src/modules/graphics/opengl/VertexBuffer.h

 	// The VBO identifier. Assigned by OpenGL.
 	GLuint vbo;
 
-	// *May* contain data saved by 'unload'.
-	char *buffer_copy;
+	// A pointer to mapped memory. Will be inialized on the first
+	// call to map().
+	void *memory_map;
 
-	// A pointer to mapped memory. Zero if memory is currently
-	// not mapped.
-	void *mapped;
+	// Set if the vbo currently operates on main instead of gpu
+	// memory.
+	bool is_mapped;
+
+	// Set if the buffer was modified while operating on gpu memory
+	// and needs to be synchronized.
+	bool is_dirty;
 
 	// Usage hint for map()/unmap() pair. Same as `access' parameter in
 	// glBufferData or 0 if not mapped.
 	GLenum mapped_access;
 };
 
+/**
+ * VertexIndex manages one shared VertexBuffer that stores the indices for an
+ * element array. Vertex arrays using the vertex structure (or anything else
+ * that can use the pattern below) can request a size and use it for the
+ * drawElements call.
+ *
+ *  indices[i*6 + 0] = i*4 + 0;
+ *  indices[i*6 + 1] = i*4 + 1;
+ *  indices[i*6 + 2] = i*4 + 2;
+ *
+ *  indices[i*6 + 3] = i*4 + 0;
+ *  indices[i*6 + 4] = i*4 + 2;
+ *  indices[i*6 + 5] = i*4 + 3;
+ *
+ * There will always be a large enough VertexBuffer around until all
+ * VertexIndex instances have been deleted.
+ *
+ * Q: Why have something like VertexIndex?
+ * A: The indices for the SpriteBatch do not change, only the array size
+ * varies. Using one VertexBuffer for all element arrays removes this
+ * duplicated data and saves some memory.
+ */
+class VertexIndex
+{
+public:
+	/**
+	 * Adds an entry to the list of sizes and resizes the VertexBuffer
+	 * if needed. A size of 1 allocates a group of 6 indices for 4 vertices
+	 * creating 1 face.
+	 *
+	 * @param size The requested size in groups of 6 indices.
+	 */
+	VertexIndex(size_t size);
+
+	/**
+	 * Removes an entry from the list of sizes and resizes the VertexBuffer
+	 * if needed.
+	 */
+	~VertexIndex();
+
+	/**
+	 * Returns the number of index groups.
+	 * This can be used for getIndexCount to get the full count of indices.
+	 *
+	 * @return The number of index groups.
+	 */
+	size_t getSize() const;
+
+	/**
+	 * Returns the number of indices that the passed element count will have.
+	 * Use VertexIndex::getSize to get the full index count for that
+	 * VertexIndex instance.
+	 *
+	 * @param elements The number of elements to calculate the index count for.
+	 * @return The index count.
+	 */
+	size_t getIndexCount(size_t elements) const;
+
+	/**
+	 * Returns the integer type of the element array.
+	 * If an optional nonzero size argument is passed, the function returns
+	 * the integer type of the element array of that size.
+	 *
+	 * @param s The size of the array to calculated the integer type of.
+	 * @return The element array integer type.
+	 */
+	GLenum getType(size_t s) const;
+	inline GLenum getType() const
+	{
+		return getType(maxSize);
+	}
+
+	/**
+	 * Returns the pointer to the VertexBuffer.
+	 * The pointer will change if a new size request or removal causes
+	 * a VertexBuffer resize. It is recommended to retrieve the pointer
+	 * value directly before the drawing call.
+	 *
+	 * @return The pointer to the VertexBuffer.
+	 */
+	VertexBuffer *getVertexBuffer() const;
+
+	/**
+	 * Returns a pointer which represents the specified byte offset.
+	 *
+	 * @param offset The offset in bytes.
+	 * @return A pointer which represents the offset.
+	 */
+	const void *getPointer(size_t offset) const;
+
+private:
+
+	/**
+	 * Adds a new size to the size list, then sorts and resizes it if needed.
+	 *
+	 * @param newSize The new size to be added.
+	 */
+	void addSize(size_t newSize);
+
+	/**
+	 * Removes a size from the size list, then sorts and resizes it if needed.
+	 *
+	 * @param oldSize The old size to be removed.
+	 */
+	void removeSize(size_t oldSize);
+
+	/**
+	 * Resizes the VertexBuffer to the requested size.
+	 * This function takes care of choosing the correct integer type and
+	 * allocating and deleting the VertexBuffer instance. It also has some
+	 * fallback logic in case the memory ran out.
+	 *
+	 * @param size The requested VertexBuffer size. Passing 0 deletes the VertexBuffer without allocating a new one.
+	 */
+	void resize(size_t size);
+
+	/**
+	 * Adds all indices to the array with the type T.
+	 * There are no checks for the correct types or overflows. The calling
+	 * function should check for that.
+	 */
+	template <typename T> void fill();
+
+	// The size of the array requested by this instance.
+	size_t size;
+
+	// The current VertexBuffer size. 0 means no VertexBuffer.
+	static size_t maxSize;
+	// The list of sizes. Needs to be kept sorted in ascending order.
+	static std::list<size_t> sizeRefs;
+	// The VertexBuffer for the element array. Can be NULL.
+	static VertexBuffer *element_array;
+};
+
 } // opengl
 } // graphics
 } // love

src/modules/image/ImageData.cpp

 	if (sw == getWidth() && getWidth() == src->getWidth()
 			&& sh == getHeight() && getHeight() == src->getHeight())
 		memcpy(d, s, sizeof(pixel) * sw * sh);
-	else if (sw > 0 && sh > 0)  // Otherwise, copy each row individually
+	else if (sw > 0)  // Otherwise, copy each row individually
 	{
 		for (int i = 0; i < sh; i++)
 		{

src/modules/physics/box2d/Physics.cpp

 {
 	Fixture *fixtureA = luax_checktype<Fixture>(L, 1, "Fixture", PHYSICS_FIXTURE_T);
 	Fixture *fixtureB = luax_checktype<Fixture>(L, 2, "Fixture", PHYSICS_FIXTURE_T);
+	b2DistanceProxy pA, pB;
 	b2DistanceInput i;
-	b2DistanceProxy pA;
-	pA.Set(fixtureA->fixture->GetShape(), 0);
-	b2DistanceProxy pB;
-	pB.Set(fixtureB->fixture->GetShape(), 0);
-	i.proxyA = pA;
-	i.proxyB = pB;
-	i.transformA = fixtureA->fixture->GetBody()->GetTransform();
-	i.transformB = fixtureB->fixture->GetBody()->GetTransform();
-	i.useRadii = true;
 	b2DistanceOutput o;
 	b2SimplexCache c;
-	b2Distance(&o, &c, &i);
+	c.count = 0;
+	try
+	{
+		pA.Set(fixtureA->fixture->GetShape(), 0);
+		pB.Set(fixtureB->fixture->GetShape(), 0);
+		i.proxyA = pA;
+		i.proxyB = pB;
+		i.transformA = fixtureA->fixture->GetBody()->GetTransform();
+		i.transformB = fixtureB->fixture->GetBody()->GetTransform();
+		i.useRadii = true;
+		b2Distance(&o, &c, &i);
+	}
+	catch (love::Exception &e)
+	{
+		luaL_error(L, "%s", e.what());
+	}
 	lua_pushnumber(L, Physics::scaleUp(o.distance));
 	lua_pushnumber(L, Physics::scaleUp(o.pointA.x));
 	lua_pushnumber(L, Physics::scaleUp(o.pointA.y));
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.