From c70b0a0b03f7bd80925ac80f0ac7ee2693ac8be6 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sun, 19 Feb 2023 19:36:03 -0400 Subject: [PATCH] Add Buffer:clear, to reset all or part of a Buffer's contents to 0. --- src/modules/graphics/Buffer.h | 5 ++++ src/modules/graphics/metal/Buffer.h | 1 + src/modules/graphics/metal/Buffer.mm | 28 ++++++++++++++++++- src/modules/graphics/opengl/Buffer.cpp | 38 +++++++++++++++++++++++++- src/modules/graphics/opengl/Buffer.h | 1 + src/modules/graphics/vulkan/Buffer.cpp | 14 ++++++++++ src/modules/graphics/vulkan/Buffer.h | 1 + src/modules/graphics/wrap_Buffer.cpp | 19 +++++++++++++ 8 files changed, 105 insertions(+), 2 deletions(-) diff --git a/src/modules/graphics/Buffer.h b/src/modules/graphics/Buffer.h index 4d8d717d8..5ca409413 100644 --- a/src/modules/graphics/Buffer.h +++ b/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. **/ diff --git a/src/modules/graphics/metal/Buffer.h b/src/modules/graphics/metal/Buffer.h index be0aaf25e..1bd6d46e2 100644 --- a/src/modules/graphics/metal/Buffer.h +++ b/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; } diff --git a/src/modules/graphics/metal/Buffer.mm b/src/modules/graphics/metal/Buffer.mm index 17e9a10fb..c797ffa6d 100644 --- a/src/modules/graphics/metal/Buffer.mm +++ b/src/modules/graphics/metal/Buffer.mm @@ -104,7 +104,16 @@ Buffer::Buffer(love::graphics::Graphics *gfx, id 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(); diff --git a/src/modules/graphics/opengl/Buffer.cpp b/src/modules/graphics/opengl/Buffer.cpp index 913eaa816..787caf630 100644 --- a/src/modules/graphics/opengl/Buffer.cpp +++ b/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 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 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? diff --git a/src/modules/graphics/opengl/Buffer.h b/src/modules/graphics/opengl/Buffer.h index 7e11706a8..acee82aa7 100644 --- a/src/modules/graphics/opengl/Buffer.h +++ b/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; }; diff --git a/src/modules/graphics/vulkan/Buffer.cpp b/src/modules/graphics/vulkan/Buffer.cpp index 31b130cd9..6a6226db1 100644 --- a/src/modules/graphics/vulkan/Buffer.cpp +++ b/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(); diff --git a/src/modules/graphics/vulkan/Buffer.h b/src/modules/graphics/vulkan/Buffer.h index 23f03dbbd..e6dd674b3 100644 --- a/src/modules/graphics/vulkan/Buffer.h +++ b/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; diff --git a/src/modules/graphics/wrap_Buffer.cpp b/src/modules/graphics/wrap_Buffer.cpp index 1f06411a0..c9bb30d4d 100644 --- a/src/modules/graphics/wrap_Buffer.cpp +++ b/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 },