Browse Source

Add love.graphics.copyBuffer.

Alex Szpakowski 4 years ago
parent
commit
e2838131f2

+ 5 - 0
src/common/Range.h

@@ -61,6 +61,11 @@ struct Range
 		return first <= other.first && last >= other.last;
 	}
 
+	bool intersects(const Range &other)
+	{
+		return !(first > other.last || last < other.first);
+	}
+
 	void encapsulate(size_t index)
 	{
 		first = std::min(first, index);

+ 4 - 0
src/modules/graphics/Buffer.cpp

@@ -50,6 +50,7 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
 	bool vertexbuffer = settings.typeFlags & TYPEFLAG_VERTEX;
 	bool texelbuffer = settings.typeFlags & TYPEFLAG_TEXEL;
 	bool storagebuffer = settings.typeFlags & TYPEFLAG_SHADER_STORAGE;
+	bool copydest = settings.typeFlags & TYPEFLAG_COPY_DEST;
 
 	if (!indexbuffer && !vertexbuffer && !texelbuffer && !storagebuffer)
 		throw love::Exception("Buffer must be created with at least one buffer type (index, vertex, texel, or shaderstorage).");
@@ -60,6 +61,9 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
 	if (storagebuffer && !caps.features[Graphics::FEATURE_GLSL4])
 		throw love::Exception("Shader Storage buffers are not supported on this system (GLSL 4 support is necessary.)");
 
+	if (copydest && usage == BUFFERUSAGE_STREAM)
+		throw love::Exception("Buffers created with 'stream' usage cannot be a copy destination.");
+
 	size_t offset = 0;
 	size_t stride = 0;
 	size_t structurealignment = 1;

+ 7 - 0
src/modules/graphics/Buffer.h

@@ -62,6 +62,8 @@ public:
 		TYPEFLAG_INDEX = 1 << BUFFERTYPE_INDEX,
 		TYPEFLAG_TEXEL = 1 << BUFFERTYPE_TEXEL,
 		TYPEFLAG_SHADER_STORAGE = 1 << BUFFERTYPE_SHADER_STORAGE,
+		TYPEFLAG_COPY_SOURCE = 1 << BUFFERTYPE_COPY_SOURCE,
+		TYPEFLAG_COPY_DEST = 1 << BUFFERTYPE_COPY_DEST,
 	};
 
 	struct DataDeclaration
@@ -136,6 +138,11 @@ public:
 	 */
 	virtual void fill(size_t offset, size_t size, const void *data) = 0;
 
+	/**
+	 * Copy a portion of this Buffer's data to another buffer, using the GPU.
+	 **/
+	virtual void copyTo(Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size) = 0;
+
 	/**
 	 * Texel buffers may use an additional texture handle as well as a buffer
 	 * handle.

+ 27 - 0
src/modules/graphics/Graphics.cpp

@@ -1039,6 +1039,32 @@ void Graphics::captureScreenshot(const ScreenshotInfo &info)
 	pendingScreenshotCallbacks.push_back(info);
 }
 
+void Graphics::copyBuffer(Buffer *source, Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size)
+{
+	if (!capabilities.features[FEATURE_COPY_BUFFER])
+		throw love::Exception("Buffer copying is not supported on this system.");
+
+	if (!(source->getTypeFlags() & Buffer::TYPEFLAG_COPY_SOURCE))
+		throw love::Exception("Copy source buffer must be created with the copysource flag.");
+
+	if (!(dest->getTypeFlags() & Buffer::TYPEFLAG_COPY_DEST))
+		throw love::Exception("Copy destination buffer must be created with the copydest flag.");
+
+	Range sourcerange(sourceoffset, size);
+	Range destrange(destoffset, size);
+
+	if (sourcerange.getMax() >= source->getSize())
+		throw love::Exception("Buffer copy source offset and size doesn't fit within the source Buffer's size.");
+
+	if (destrange.getMax() >= dest->getSize())
+		throw love::Exception("Buffer copy destination offset and size doesn't fit within the destination buffer's size.");
+
+	if (source == dest && sourcerange.intersects(destrange))
+		throw love::Exception("Copying a portion of a buffer to the same buffer requires non-overlapping source and destination offsets.");
+
+	source->copyTo(dest, sourceoffset, destoffset, size);
+}
+
 Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawCommand &cmd)
 {
 	BatchedDrawState &state = batchedDrawState;
@@ -1835,6 +1861,7 @@ STRINGMAP_CLASS_BEGIN(Graphics, Graphics::Feature, Graphics::FEATURE_MAX_ENUM, f
 	{ "glsl4",                    Graphics::FEATURE_GLSL4                },
 	{ "instancing",               Graphics::FEATURE_INSTANCING           },
 	{ "texelbuffer",              Graphics::FEATURE_TEXEL_BUFFER         },
+	{ "copybuffer",               Graphics::FEATURE_COPY_BUFFER          },
 }
 STRINGMAP_CLASS_END(Graphics, Graphics::Feature, Graphics::FEATURE_MAX_ENUM, feature)
 

+ 3 - 0
src/modules/graphics/Graphics.h

@@ -144,6 +144,7 @@ public:
 		FEATURE_GLSL4,
 		FEATURE_INSTANCING,
 		FEATURE_TEXEL_BUFFER,
+		FEATURE_COPY_BUFFER,
 		FEATURE_MAX_ENUM
 	};
 
@@ -668,6 +669,8 @@ public:
 
 	void captureScreenshot(const ScreenshotInfo &info);
 
+	void copyBuffer(Buffer *source, Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size);
+
 	void draw(Drawable *drawable, const Matrix4 &m);
 	void draw(Texture *texture, Quad *quad, const Matrix4 &m);
 	void drawLayer(Texture *texture, int layer, const Matrix4 &m);

+ 8 - 0
src/modules/graphics/opengl/Buffer.cpp

@@ -253,6 +253,14 @@ void Buffer::fill(size_t offset, size_t size, const void *data)
 	}
 }
 
+void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size)
+{
+	gl.bindBuffer(BUFFERTYPE_COPY_SOURCE, buffer);
+	gl.bindBuffer(BUFFERTYPE_COPY_DEST, ((Buffer *) dest)->buffer);
+
+	glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, sourceoffset, destoffset, size);
+}
+
 } // opengl
 } // graphics
 } // love

+ 1 - 0
src/modules/graphics/opengl/Buffer.h

@@ -53,6 +53,7 @@ public:
 	void *map(MapType map, size_t offset, size_t size) override;
 	void unmap(size_t usedoffset, size_t usedsize) override;
 	void fill(size_t offset, size_t size, const void *data) override;
+	void copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size) override;
 
 	ptrdiff_t getHandle() const override { return buffer; };
 	ptrdiff_t getTexelBufferHandle() const override { return texture; };

+ 2 - 1
src/modules/graphics/opengl/Graphics.cpp

@@ -1531,7 +1531,8 @@ void Graphics::initCapabilities()
 	capabilities.features[FEATURE_GLSL4] = GLAD_ES_VERSION_3_1 || (gl.isCoreProfile() && GLAD_VERSION_4_3);
 	capabilities.features[FEATURE_INSTANCING] = gl.isInstancingSupported();
 	capabilities.features[FEATURE_TEXEL_BUFFER] = gl.isBufferTypeSupported(BUFFERTYPE_TEXEL);
-	static_assert(FEATURE_MAX_ENUM == 11, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
+	capabilities.features[FEATURE_COPY_BUFFER] = gl.isBufferTypeSupported(BUFFERTYPE_COPY_SOURCE);
+	static_assert(FEATURE_MAX_ENUM == 12, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
 
 	capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize();
 	capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize();

+ 5 - 0
src/modules/graphics/opengl/OpenGL.cpp

@@ -621,6 +621,8 @@ GLenum OpenGL::getGLBufferType(BufferType type)
 		case BUFFERTYPE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
 		case BUFFERTYPE_TEXEL: return GL_TEXTURE_BUFFER;
 		case BUFFERTYPE_SHADER_STORAGE: return GL_SHADER_STORAGE_BUFFER;
+		case BUFFERTYPE_COPY_SOURCE: return GL_COPY_READ_BUFFER;
+		case BUFFERTYPE_COPY_DEST: return GL_COPY_WRITE_BUFFER;
 		case BUFFERTYPE_MAX_ENUM: return GL_ZERO;
 	}
 
@@ -1444,6 +1446,9 @@ bool OpenGL::isBufferTypeSupported(BufferType type) const
 		return GLAD_VERSION_3_1;
 	case BUFFERTYPE_SHADER_STORAGE:
 		return (GLAD_VERSION_4_3 && isCoreProfile()) || GLAD_ES_VERSION_3_1;
+	case BUFFERTYPE_COPY_SOURCE:
+	case BUFFERTYPE_COPY_DEST:
+		return GLAD_VERSION_3_1 || GLAD_ES_VERSION_3_0;
 	case BUFFERTYPE_MAX_ENUM:
 		return false;
 	}

+ 2 - 0
src/modules/graphics/vertex.cpp

@@ -343,6 +343,8 @@ STRINGMAP_BEGIN(BufferType, BUFFERTYPE_MAX_ENUM, bufferTypeName)
 	{ "index",         BUFFERTYPE_INDEX          },
 	{ "texel",         BUFFERTYPE_TEXEL          },
 	{ "shaderstorage", BUFFERTYPE_SHADER_STORAGE },
+	{ "copysource",    BUFFERTYPE_COPY_SOURCE    },
+	{ "copydest",      BUFFERTYPE_COPY_DEST      },
 }
 STRINGMAP_END(BufferType, BUFFERTYPE_MAX_ENUM, bufferTypeName)
 

+ 2 - 0
src/modules/graphics/vertex.h

@@ -60,6 +60,8 @@ enum BufferType
 	BUFFERTYPE_INDEX,
 	BUFFERTYPE_TEXEL,
 	BUFFERTYPE_SHADER_STORAGE,
+	BUFFERTYPE_COPY_SOURCE,
+	BUFFERTYPE_COPY_DEST,
 	BUFFERTYPE_MAX_ENUM
 };
 

+ 23 - 0
src/modules/graphics/wrap_Graphics.cpp

@@ -3255,6 +3255,27 @@ int w_polygon(lua_State *L)
 	return 0;
 }
 
+int w_copyBuffer(lua_State *L)
+{
+	Buffer *source = luax_checkbuffer(L, 1);
+	Buffer *dest = luax_checkbuffer(L, 2);
+
+	ssize_t sourceoffset = luaL_optinteger(L, 3, 0);
+	ssize_t destoffset = luaL_optinteger(L, 4, 0);
+
+	ssize_t size = std::min(source->getSize() - sourceoffset, dest->getSize() - destoffset);
+	if (!lua_isnoneornil(L, 5))
+		size = luaL_checkinteger(L, 5);
+
+	if (sourceoffset < 0 || destoffset < 0)
+		return luaL_error(L, "copyBuffer offsets cannot be negative.");
+	if (size <= 0)
+		return luaL_error(L, "copyBuffer size must be greater than 0.");
+
+	luax_catchexcept(L, [&](){ instance()->copyBuffer(source, dest, sourceoffset, destoffset, size); });
+	return 0;
+}
+
 int w_flushBatch(lua_State *)
 {
 	instance()->flushBatchedDraws();
@@ -3451,6 +3472,8 @@ static const luaL_Reg functions[] =
 	{ "print", w_print },
 	{ "printf", w_printf },
 
+	{ "copyBuffer", w_copyBuffer },
+
 	{ "isCreated", w_isCreated },
 	{ "isActive", w_isActive },
 	{ "isGammaCorrect", w_isGammaCorrect },