Browse Source

Add Buffer:clear, to reset all or part of a Buffer's contents to 0.

Sasha Szpakowski 2 years ago
parent
commit
c70b0a0b03

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

@@ -131,6 +131,11 @@ public:
 	 */
 	virtual bool fill(size_t offset, size_t size, const void *data) = 0;
 
+	/**
+	 * Reset the given portion of this buffer's data to 0.
+	 */
+	virtual void clear(size_t offset, size_t size) = 0;
+
 	/**
 	 * Copy a portion of this Buffer's data to another buffer, using the GPU.
 	 **/

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

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

+ 27 - 1
src/modules/graphics/metal/Buffer.mm

@@ -104,7 +104,16 @@ Buffer::Buffer(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settin
 	{
 		auto *mgfx = (Graphics *) gfx;
 		auto encoder = mgfx->useBlitEncoder();
-		[encoder fillBuffer:buffer range:NSMakeRange(0, size) value:0];
+
+		size_t clearsize = size;
+
+#ifdef LOVE_MACOS
+		// Metal limitation on macOS.
+		clearsize -= (clearsize % 4);
+#endif
+
+		if (clearsize > 0)
+			[encoder fillBuffer:buffer range:NSMakeRange(0, clearsize) value:0];
 	}
 }}
 
@@ -197,6 +206,23 @@ bool Buffer::fill(size_t offset, size_t size, const void *data)
 	return true;
 }}
 
+void Buffer::clear(size_t offset, size_t size)
+{ @autoreleasepool {
+	if (isImmutable())
+		throw love::Exception("Cannot clear an immutable Buffer.");
+	else if (isMapped())
+		throw love::Exception("Cannot clear a mapped Buffer.");
+	else if (offset + size > getSize())
+		throw love::Exception("The given offset and size parameters to clear() are not within the Buffer's size.");
+	else if (offset % 4 != 0 || size % 4 != 0)
+		throw love::Exception("clear() must be used with offset and size parameters that are multiples of 4 bytes.");
+
+	auto gfx = Graphics::getInstance();
+	auto encoder = gfx->useBlitEncoder();
+
+	[encoder fillBuffer:buffer range:NSMakeRange(offset, size) value:0];
+}}
+
 void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size)
 { @autoreleasepool {
 	auto gfx = Graphics::getInstance();

+ 37 - 1
src/modules/graphics/opengl/Buffer.cpp

@@ -89,7 +89,7 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
 		ownsMemoryMap = true;
 
 	std::vector<uint8> emptydata;
-	if (settings.zeroInitialize && data == nullptr)
+	if (settings.zeroInitialize && data == nullptr && !GLAD_VERSION_4_3)
 	{
 		try
 		{
@@ -107,6 +107,12 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
 		unloadVolatile();
 		throw love::Exception("Could not create buffer with %d bytes (out of VRAM?)", size);
 	}
+
+	if (settings.zeroInitialize && data == nullptr && GLAD_VERSION_4_3)
+	{
+		gl.bindBuffer(mapUsage, buffer);
+		glClearBufferData(target, GL_R8UI, GL_RED, GL_UNSIGNED_BYTE, nullptr);
+	}
 }
 
 Buffer::~Buffer()
@@ -290,6 +296,36 @@ bool Buffer::fill(size_t offset, size_t size, const void *data)
 	return true;
 }
 
+void Buffer::clear(size_t offset, size_t size)
+{
+	if (isImmutable())
+		throw love::Exception("Cannot clear an immutable Buffer.");
+	else if (isMapped())
+		throw love::Exception("Cannot clear a mapped Buffer.");
+	else if (offset + size > getSize())
+		throw love::Exception("The given offset and size parameters to clear() are not within the Buffer's size.");
+	else if (offset % 4 != 0 || size % 4 != 0)
+		throw love::Exception("clear() must be used with offset and size parameters that are multiples of 4 bytes.");
+
+	if (GLAD_VERSION_4_3)
+	{
+		gl.bindBuffer(mapUsage, buffer);
+		glClearBufferSubData(target, GL_R8UI, offset, size, GL_RED, GL_UNSIGNED_BYTE, nullptr);
+	}
+	else
+	{
+		try
+		{
+			std::vector<uint8> emptydata(getSize());
+			fill(0, getSize(), emptydata.data());
+		}
+		catch (std::exception &)
+		{
+			throw love::Exception("Out of memory.");
+		}
+	}
+}
+
 void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size)
 {
 	// TODO: tracked state for these bind types?

+ 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;
 	bool fill(size_t offset, size_t size, const void *data) override;
+	void clear(size_t offset, size_t size) override;
 	void copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size) override;
 
 	ptrdiff_t getHandle() const override { return buffer; };

+ 14 - 0
src/modules/graphics/vulkan/Buffer.cpp

@@ -254,6 +254,20 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize)
 	}
 }
 
+void Buffer::clear(size_t offset, size_t size)
+{
+	if (isImmutable())
+		throw love::Exception("Cannot clear an immutable Buffer.");
+	else if (isMapped())
+		throw love::Exception("Cannot clear a mapped Buffer.");
+	else if (offset + size > getSize())
+		throw love::Exception("The given offset and size parameters to clear() are not within the Buffer's size.");
+	else if (offset % 4 != 0 || size % 4 != 0)
+		throw love::Exception("clear() must be used with offset and size parameters that are multiples of 4 bytes.");
+
+	vkCmdFillBuffer(vgfx->getCommandBufferForDataTransfer(), buffer, offset, size, 0);
+}
+
 void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size)
 {
 	auto commandBuffer = vgfx->getCommandBufferForDataTransfer();

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

@@ -51,6 +51,7 @@ public:
 	void *map(MapType map, size_t offset, size_t size) override;
 	void unmap(size_t usedoffset, size_t usedsize) override;
 	bool fill(size_t offset, size_t size, const void *data) override;
+	void clear(size_t offset, size_t size) override;
 	void copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size) override;
 	ptrdiff_t getHandle() const override;
 	ptrdiff_t getTexelBufferHandle() const override;

+ 19 - 0
src/modules/graphics/wrap_Buffer.cpp

@@ -318,6 +318,24 @@ static int w_Buffer_setArrayData(lua_State *L)
 	return 0;
 }
 
+static int w_Buffer_clear(lua_State *L)
+{
+	Buffer *t = luax_checkbuffer(L, 1);
+	size_t offset = 0;
+	size_t size = t->getSize();
+	if (!lua_isnoneornil(L, 2))
+	{
+		lua_Number offsetp = luaL_checknumber(L, 2);
+		lua_Number sizep = luaL_checknumber(L, 3);
+		if (offsetp < 0 || sizep < 0)
+			return luaL_error(L, "Offset and size parameters cannot be negative.");
+		offset = (size_t) offsetp;
+		size = (size_t) sizep;
+	}
+	luax_catchexcept(L, [&]() { t->clear(offset, size); });
+	return 0;
+}
+
 static int w_Buffer_getElementCount(lua_State *L)
 {
 	Buffer *t = luax_checkbuffer(L, 1);
@@ -386,6 +404,7 @@ static int w_Buffer_isBufferType(lua_State *L)
 static const luaL_Reg w_Buffer_functions[] =
 {
 	{ "setArrayData", w_Buffer_setArrayData },
+	{ "clear", w_Buffer_clear },
 	{ "getElementCount", w_Buffer_getElementCount },
 	{ "getElementStride", w_Buffer_getElementStride },
 	{ "getSize", w_Buffer_getSize },