Browse Source

Replace internal 'staging' buffer data type with new 'readback' type.

Alex Szpakowski 3 years ago
parent
commit
a533982cb4

+ 3 - 2
src/modules/graphics/Buffer.cpp

@@ -36,6 +36,7 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
 	, usageFlags(settings.usageFlags)
 	, dataUsage(settings.dataUsage)
 	, mapped(false)
+	, mappedType(MAP_WRITE_INVALIDATE)
 	, immutable(false)
 {
 	if (size == 0 && arraylength == 0)
@@ -61,8 +62,8 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
 	if (storagebuffer && dataUsage == BUFFERDATAUSAGE_STREAM)
 		throw love::Exception("Buffers created with 'stream' data usage cannot be used as a shader storage buffer.");
 
-	if (dataUsage == BUFFERDATAUSAGE_STAGING && (indexbuffer || vertexbuffer || texelbuffer || storagebuffer))
-		throw love::Exception("Buffers created with 'staging' data usage cannot be index, vertex, texel, or shaderstorage buffer types.");
+	if (dataUsage == BUFFERDATAUSAGE_READBACK && (indexbuffer || vertexbuffer || texelbuffer || storagebuffer))
+		throw love::Exception("Buffers created with 'readback' data usage cannot be index, vertex, texel, or shaderstorage buffer types.");
 
 	size_t offset = 0;
 	size_t stride = 0;

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

@@ -53,6 +53,7 @@ public:
 	enum MapType
 	{
 		MAP_WRITE_INVALIDATE,
+		MAP_READ_ONLY,
 	};
 
 	struct DataDeclaration
@@ -128,7 +129,7 @@ public:
 	/**
 	 * Fill a portion of the buffer with data.
 	 */
-	virtual void fill(size_t offset, size_t size, const void *data) = 0;
+	virtual bool 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.
@@ -147,10 +148,10 @@ public:
 	{
 	public:
 
-		Mapper(Buffer &buffer)
+		Mapper(Buffer &buffer, MapType maptype = MAP_WRITE_INVALIDATE)
 			: buffer(buffer)
 		{
-			data = buffer.map(MAP_WRITE_INVALIDATE, 0, buffer.getSize());
+			data = buffer.map(maptype, 0, buffer.getSize());
 		}
 
 		~Mapper()
@@ -179,7 +180,7 @@ protected:
 	BufferDataUsage dataUsage;
 
 	bool mapped;
-
+	MapType mappedType;
 	bool immutable;
 	
 }; // Buffer

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

@@ -1181,6 +1181,9 @@ void Graphics::copyBuffer(Buffer *source, Buffer *dest, size_t sourceoffset, siz
 	if (dest->getDataUsage() == 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))

+ 1 - 1
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; }

+ 38 - 24
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<MTLDevice> 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)

+ 35 - 8
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)

+ 3 - 1
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);

+ 1 - 1
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;
 	}

+ 4 - 4
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)
 

+ 1 - 1
src/modules/graphics/vertex.h

@@ -110,7 +110,7 @@ enum BufferDataUsage
 	BUFFERDATAUSAGE_STREAM,
 	BUFFERDATAUSAGE_DYNAMIC,
 	BUFFERDATAUSAGE_STATIC,
-	BUFFERDATAUSAGE_STAGING,
+	BUFFERDATAUSAGE_READBACK,
 	BUFFERDATAUSAGE_MAX_ENUM
 };