diff --git a/src/modules/graphics/Buffer.cpp b/src/modules/graphics/Buffer.cpp index 89c3f0704..c8ab11d7b 100644 --- a/src/modules/graphics/Buffer.cpp +++ b/src/modules/graphics/Buffer.cpp @@ -36,6 +36,7 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vectorgetDataUsage() == BUFFERDATAUSAGE_STREAM) throw love::Exception("Buffers created with 'stream' data usage cannot be used as a copy destination."); + if (source->getDataUsage() == BUFFERDATAUSAGE_READBACK) + throw love::Exception("Buffers created with 'readback' data usage cannot be used as a copy source."); + if (sourcerange.getMax() >= source->getSize()) throw love::Exception("Buffer copy source offset and size doesn't fit within the source Buffer's size."); @@ -1290,6 +1293,9 @@ void Graphics::copyBufferToTexture(Buffer *source, Texture *dest, size_t sourceo if (!capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE]) throw love::Exception("Copying a Buffer to a Texture is not supported on this system."); + if (source->getDataUsage() == BUFFERDATAUSAGE_READBACK) + throw love::Exception("Buffers created with 'readback' data usage cannot be used as a copy source."); + PixelFormat format = dest->getPixelFormat(); if (isPixelFormatDepthStencil(format)) diff --git a/src/modules/graphics/metal/Buffer.h b/src/modules/graphics/metal/Buffer.h index 440114137..40f1c64ac 100644 --- a/src/modules/graphics/metal/Buffer.h +++ b/src/modules/graphics/metal/Buffer.h @@ -40,7 +40,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; + bool 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 (ptrdiff_t) buffer; } diff --git a/src/modules/graphics/metal/Buffer.mm b/src/modules/graphics/metal/Buffer.mm index 07cc47f3a..bac55cb1c 100644 --- a/src/modules/graphics/metal/Buffer.mm +++ b/src/modules/graphics/metal/Buffer.mm @@ -18,7 +18,7 @@ * 3. This notice may not be removed or altered from any source distribution. **/ -#import "Buffer.h" +#include "Buffer.h" #include "Graphics.h" namespace love @@ -65,11 +65,16 @@ Buffer::Buffer(love::graphics::Graphics *gfx, id device, const Settin size = getSize(); arraylength = getArrayLength(); - MTLResourceOptions opts = MTLResourceStorageModePrivate; + MTLResourceOptions opts = 0; + if (settings.dataUsage == BUFFERDATAUSAGE_READBACK) + opts |= MTLResourceStorageModeShared; + else + opts |= MTLResourceStorageModePrivate; + buffer = [device newBufferWithLength:size options:opts]; if (buffer == nil) - throw love::Exception("Could not create buffer (out of VRAM?)"); + throw love::Exception("Could not create buffer with %d bytes (out of VRAM?)", size); if (usageFlags & BUFFERUSAGEFLAG_TEXEL) { @@ -109,16 +114,30 @@ Buffer::~Buffer() texture = nil; }} -void *Buffer::map(MapType /*map*/, size_t offset, size_t size) +void *Buffer::map(MapType map, size_t offset, size_t size) { @autoreleasepool { - if (size == 0 || isImmutable()) + if (size == 0) return nullptr; + if (map == MAP_WRITE_INVALIDATE && (isImmutable() || dataUsage == BUFFERDATAUSAGE_READBACK)) + return nullptr; + + if (map == MAP_READ_ONLY && dataUsage != BUFFERDATAUSAGE_READBACK) + return nullptr; + Range r(offset, size); if (!Range(0, getSize()).contains(r)) return nullptr; + if (map == MAP_READ_ONLY) + { + mappedRange = r; + mapped = true; + mappedType = map; + return (char *) buffer.contents + offset; + } + auto gfx = Graphics::getInstance(); // TODO: Don't create a new buffer every time, also do something for stream @@ -129,6 +148,7 @@ void *Buffer::map(MapType /*map*/, size_t offset, size_t size) { mappedRange = r; mapped = true; + mappedType = map; return mapBuffer.contents; } @@ -137,6 +157,12 @@ void *Buffer::map(MapType /*map*/, size_t offset, size_t size) void Buffer::unmap(size_t usedoffset, size_t usedsize) { @autoreleasepool { + if (mappedType == MAP_READ_ONLY) + { + mapped = false; + return; + } + if (mapBuffer == nil) return; @@ -158,29 +184,17 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize) mapped = false; }} -void Buffer::fill(size_t offset, size_t size, const void *data) +bool Buffer::fill(size_t offset, size_t size, const void *data) { @autoreleasepool { - if (size == 0 || isImmutable()) - return; + void *dest = map(MAP_WRITE_INVALIDATE, offset, size); - size_t buffersize = getSize(); + if (dest == nullptr) + return false; - if (!Range(0, buffersize).contains(Range(offset, size))) - return; + memcpy(dest, data, size); - // TODO: Don't create a new buffer every time, also do something for stream - // buffers. - auto gfx = Graphics::getInstance(); - auto encoder = gfx->useBlitEncoder(); - - auto tempbuffer = [gfx->device newBufferWithLength:size options:MTLResourceStorageModeShared]; - memcpy(tempbuffer.contents, data, size); - - [encoder copyFromBuffer:tempbuffer - sourceOffset:0 - toBuffer:buffer - destinationOffset:offset - size:size]; + unmap(offset, size); + return true; }} void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size) diff --git a/src/modules/graphics/opengl/Buffer.cpp b/src/modules/graphics/opengl/Buffer.cpp index 3750312ad..3a6cf74ff 100644 --- a/src/modules/graphics/opengl/Buffer.cpp +++ b/src/modules/graphics/opengl/Buffer.cpp @@ -103,7 +103,7 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st if (!load(data)) { unloadVolatile(); - throw love::Exception("Could not create buffer (out of VRAM?)"); + throw love::Exception("Could not create buffer with %d bytes (out of VRAM?)", size); } } @@ -164,11 +164,17 @@ bool Buffer::supportsOrphan() const return dataUsage == BUFFERDATAUSAGE_STREAM || dataUsage == BUFFERDATAUSAGE_DYNAMIC; } -void *Buffer::map(MapType /*map*/, size_t offset, size_t size) +void *Buffer::map(MapType map, size_t offset, size_t size) { - if (size == 0 || isImmutable()) + if (size == 0) return nullptr; + if (map == MAP_WRITE_INVALIDATE && (isImmutable() || dataUsage == BUFFERDATAUSAGE_READBACK)) + return nullptr; + + if (map == MAP_READ_ONLY && dataUsage != BUFFERDATAUSAGE_READBACK) + return nullptr; + Range r(offset, size); if (!Range(0, getSize()).contains(r)) @@ -176,7 +182,16 @@ void *Buffer::map(MapType /*map*/, size_t offset, size_t size) char *data = nullptr; - if (ownsMemoryMap) + if (map == MAP_READ_ONLY) + { + gl.bindBuffer(mapUsage, buffer); + + if (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0) + data = (char *) glMapBufferRange(target, offset, size, GL_MAP_READ_BIT); + else if (GLAD_VERSION_1_1) + data = (char *) glMapBuffer(target, GL_READ_ONLY) + offset; + } + else if (ownsMemoryMap) { if (memoryMap == nullptr) memoryMap = (char *) malloc(getSize()); @@ -191,6 +206,7 @@ void *Buffer::map(MapType /*map*/, size_t offset, size_t size) if (data != nullptr) { mapped = true; + mappedType = map; mappedRange = r; if (!ownsMemoryMap) memoryMap = data; @@ -208,6 +224,15 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize) mapped = false; + if (mappedType == MAP_READ_ONLY) + { + gl.bindBuffer(mapUsage, buffer); + glUnmapBuffer(target); + if (!ownsMemoryMap) + memoryMap = nullptr; + return; + } + // Orphan optimization - see fill(). if (supportsOrphan() && mappedRange.first == 0 && mappedRange.getSize() == getSize()) { @@ -227,15 +252,15 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize) } } -void Buffer::fill(size_t offset, size_t size, const void *data) +bool Buffer::fill(size_t offset, size_t size, const void *data) { - if (size == 0 || isImmutable()) - return; + if (size == 0 || isImmutable() || dataUsage == BUFFERDATAUSAGE_READBACK) + return false; size_t buffersize = getSize(); if (!Range(0, buffersize).contains(Range(offset, size))) - return; + return false; GLenum gldatausage = OpenGL::getGLBufferDataUsage(dataUsage); @@ -259,6 +284,8 @@ void Buffer::fill(size_t offset, size_t size, const void *data) { glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data); } + + return true; } void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size) diff --git a/src/modules/graphics/opengl/Buffer.h b/src/modules/graphics/opengl/Buffer.h index 849e92018..f7678eab7 100644 --- a/src/modules/graphics/opengl/Buffer.h +++ b/src/modules/graphics/opengl/Buffer.h @@ -52,12 +52,14 @@ 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; + bool 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; }; + BufferUsage getMapUsage() const { return mapUsage; } + private: bool load(const void *initialdata); diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 164404e97..fd70ee3ba 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -843,7 +843,7 @@ GLenum OpenGL::getGLBufferDataUsage(BufferDataUsage usage) case BUFFERDATAUSAGE_STREAM: return GL_STREAM_DRAW; case BUFFERDATAUSAGE_DYNAMIC: return GL_DYNAMIC_DRAW; case BUFFERDATAUSAGE_STATIC: return GL_STATIC_DRAW; - case BUFFERDATAUSAGE_STAGING: + case BUFFERDATAUSAGE_READBACK: return (GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0) ? GL_STREAM_READ : GL_STREAM_DRAW; default: return 0; } diff --git a/src/modules/graphics/vertex.cpp b/src/modules/graphics/vertex.cpp index 6329cf30b..9896974f9 100644 --- a/src/modules/graphics/vertex.cpp +++ b/src/modules/graphics/vertex.cpp @@ -383,10 +383,10 @@ STRINGMAP_END(IndexDataType, INDEX_MAX_ENUM, indexType) STRINGMAP_BEGIN(BufferDataUsage, BUFFERDATAUSAGE_MAX_ENUM, bufferDataUsage) { - { "stream", BUFFERDATAUSAGE_STREAM }, - { "dynamic", BUFFERDATAUSAGE_DYNAMIC }, - { "static", BUFFERDATAUSAGE_STATIC }, - { "staging", BUFFERDATAUSAGE_STAGING }, + { "stream", BUFFERDATAUSAGE_STREAM }, + { "dynamic", BUFFERDATAUSAGE_DYNAMIC }, + { "static", BUFFERDATAUSAGE_STATIC }, + { "readback", BUFFERDATAUSAGE_READBACK }, } STRINGMAP_END(BufferDataUsage, BUFFERDATAUSAGE_MAX_ENUM, bufferDataUsage) diff --git a/src/modules/graphics/vertex.h b/src/modules/graphics/vertex.h index 73ed163fd..ea5b1493c 100644 --- a/src/modules/graphics/vertex.h +++ b/src/modules/graphics/vertex.h @@ -110,7 +110,7 @@ enum BufferDataUsage BUFFERDATAUSAGE_STREAM, BUFFERDATAUSAGE_DYNAMIC, BUFFERDATAUSAGE_STATIC, - BUFFERDATAUSAGE_STAGING, + BUFFERDATAUSAGE_READBACK, BUFFERDATAUSAGE_MAX_ENUM };