[Bf-blender-cvs] [b5bf3011bf3] blender2.8: GWN: Vertex Buffer: Remove the use of glMapBufferRange

Clément Foucault noreply at git.blender.org
Mon Mar 19 16:08:29 CET 2018


Commit: b5bf3011bf3851d1e68dbcdde310fff04dc3f2f5
Author: Clément Foucault
Date:   Mon Mar 19 16:13:00 2018 +0100
Branches: blender2.8
https://developer.blender.org/rBb5bf3011bf3851d1e68dbcdde310fff04dc3f2f5

GWN: Vertex Buffer: Remove the use of glMapBufferRange

We revert to the malloc/realloc and manually manage the upload.
There seems to be a performance penalty from using glMapBuffer on some
hardware, prefering way is glBufferData(NULL) with glBufferSubData.

===================================================================

M	intern/gawain/gawain/gwn_vertex_buffer.h
M	intern/gawain/src/gwn_vertex_buffer.c

===================================================================

diff --git a/intern/gawain/gawain/gwn_vertex_buffer.h b/intern/gawain/gawain/gwn_vertex_buffer.h
index d9faae4f55f..ddb77368a02 100644
--- a/intern/gawain/gawain/gwn_vertex_buffer.h
+++ b/intern/gawain/gawain/gwn_vertex_buffer.h
@@ -25,13 +25,14 @@
 typedef enum {
 	// can be extended to support more types
 	GWN_USAGE_STREAM,
-	GWN_USAGE_STATIC,
+	GWN_USAGE_STATIC, // do not keep data in memory
 	GWN_USAGE_DYNAMIC
 } Gwn_UsageType;
 
 typedef struct Gwn_VertBuf {
 	Gwn_VertFormat format;
 	unsigned vertex_ct;
+	bool dirty;
 	GLubyte* data; // NULL indicates data in VRAM (unmapped)
 	GLuint vbo_id; // 0 indicates not yet allocated
 	Gwn_UsageType usage; // usage hint for GL optimisation
@@ -53,10 +54,7 @@ void GWN_vertbuf_init_with_format_ex(Gwn_VertBuf*, const Gwn_VertFormat*, Gwn_Us
 
 unsigned GWN_vertbuf_size_get(const Gwn_VertBuf*);
 void GWN_vertbuf_data_alloc(Gwn_VertBuf*, unsigned v_ct);
-void GWN_vertbuf_data_resize_ex(Gwn_VertBuf*, unsigned v_ct, bool keep_data);
-
-#define GWN_vertbuf_data_resize(verts, v_ct) \
-	GWN_vertbuf_data_resize_ex(verts, v_ct, true)
+void GWN_vertbuf_data_resize(Gwn_VertBuf*, unsigned v_ct);
 
 // The most important set_attrib variant is the untyped one. Get it right first.
 // It takes a void* so the app developer is responsible for matching their app data types
diff --git a/intern/gawain/src/gwn_vertex_buffer.c b/intern/gawain/src/gwn_vertex_buffer.c
index 17c2a3e9364..39fc1885b92 100644
--- a/intern/gawain/src/gwn_vertex_buffer.c
+++ b/intern/gawain/src/gwn_vertex_buffer.c
@@ -52,6 +52,7 @@ void GWN_vertbuf_init(Gwn_VertBuf* verts, Gwn_UsageType usage)
 	{
 	memset(verts, 0, sizeof(Gwn_VertBuf));
 	verts->usage = usage;
+	verts->dirty = true;
 	}
 
 void GWN_vertbuf_init_with_format_ex(Gwn_VertBuf* verts, const Gwn_VertFormat* format, Gwn_UsageType usage)
@@ -72,6 +73,9 @@ void GWN_vertbuf_discard(Gwn_VertBuf* verts)
 #endif
 		}
 
+	if (verts->data)
+		free(verts->data);
+
 	free(verts);
 	}
 
@@ -80,90 +84,58 @@ unsigned GWN_vertbuf_size_get(const Gwn_VertBuf* verts)
 	return vertex_buffer_size(&verts->format, verts->vertex_ct);
 	}
 
+// create a new allocation, discarding any existing data
 void GWN_vertbuf_data_alloc(Gwn_VertBuf* verts, unsigned v_ct)
 	{
 	Gwn_VertFormat* format = &verts->format;
 	if (!format->packed)
 		VertexFormat_pack(format);
 
-	verts->vertex_ct = v_ct;
-
 #if TRUST_NO_ONE
-	assert(verts->vbo_id == 0);
+	// catch any unnecessary use
+	assert(verts->vertex_ct != v_ct || verts->data == NULL);
 #endif
 
-	unsigned buffer_sz = GWN_vertbuf_size_get(verts);
+	// only create the buffer the 1st time
+	if (verts->vbo_id == 0)
+		verts->vbo_id = GWN_buf_id_alloc();
+
+	// discard previous data if any
+	if (verts->data)
+		free(verts->data);
+
 #if VRAM_USAGE
-	vbo_memory_usage += buffer_sz;
+	vbo_memory_usage -= GWN_vertbuf_size_get(verts);
 #endif
 
-	// create an array buffer and map it to memory
-	verts->vbo_id = GWN_buf_id_alloc();
-	glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
-	glBufferData(GL_ARRAY_BUFFER, buffer_sz, NULL, convert_usage_type_to_gl(verts->usage));
-	verts->data = glMapBufferRange(GL_ARRAY_BUFFER, 0, buffer_sz, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
+	verts->dirty = true;
+	verts->vertex_ct = v_ct;
+	verts->data = malloc(sizeof(GLubyte) * GWN_vertbuf_size_get(verts));
+
+#if VRAM_USAGE
+	vbo_memory_usage += GWN_vertbuf_size_get(verts);
+#endif
 	}
 
-void GWN_vertbuf_data_resize_ex(Gwn_VertBuf* verts, unsigned v_ct, bool keep_data)
+// resize buffer keeping existing data
+void GWN_vertbuf_data_resize(Gwn_VertBuf* verts, unsigned v_ct)
 	{
 #if TRUST_NO_ONE
-	assert(verts->vbo_id != 0);
+	assert(verts->data != NULL);
+	assert(verts->vertex_ct != v_ct);
 #endif
 
-	if (verts->vertex_ct == v_ct)
-		return;
-
-	unsigned old_buf_sz = GWN_vertbuf_size_get(verts);
-	verts->vertex_ct = v_ct;
-	unsigned new_buf_sz = GWN_vertbuf_size_get(verts);
 #if VRAM_USAGE
-	vbo_memory_usage += new_buf_sz - old_buf_sz;
+	vbo_memory_usage -= GWN_vertbuf_size_get(verts);
 #endif
 
-	if (keep_data)
-		{
-		// we need to do a copy to keep the existing data
-		GLuint vbo_tmp;
-		glGenBuffers(1, &vbo_tmp);
-		// only copy the data that can fit in the new buffer
-		unsigned copy_sz = (old_buf_sz < new_buf_sz) ? old_buf_sz : new_buf_sz;
-		glBindBuffer(GL_COPY_WRITE_BUFFER, vbo_tmp);
-		glBufferData(GL_COPY_WRITE_BUFFER, copy_sz, NULL, GL_STREAM_COPY);
-
-		glBindBuffer(GL_COPY_READ_BUFFER, verts->vbo_id);
-		// we cannot copy from/to a mapped buffer
-		if (verts->data)
-			glUnmapBuffer(GL_COPY_READ_BUFFER);
-
-		// save data, resize the buffer, restore data
-		glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, copy_sz);
-		glBufferData(GL_COPY_READ_BUFFER, new_buf_sz, NULL, convert_usage_type_to_gl(verts->usage));
-		glCopyBufferSubData(GL_COPY_WRITE_BUFFER, GL_COPY_READ_BUFFER, 0, 0, copy_sz);
-
-		glDeleteBuffers(1, &vbo_tmp);
-		}
-	else
-		{
-		glBindBuffer(GL_COPY_READ_BUFFER, verts->vbo_id);
-		glBufferData(GL_COPY_READ_BUFFER, new_buf_sz, NULL, convert_usage_type_to_gl(verts->usage));
-		}
-
-	// if the buffer was mapped, update it's pointer
-	if (verts->data)
-		verts->data = glMapBufferRange(GL_COPY_READ_BUFFER, 0, new_buf_sz, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
-	}
-
-static void VertexBuffer_map(Gwn_VertBuf* verts)
-	{
-	glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
-	verts->data = glMapBufferRange(GL_ARRAY_BUFFER, 0, GWN_vertbuf_size_get(verts), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
-	}
+	verts->dirty = true;
+	verts->vertex_ct = v_ct;
+	verts->data = realloc(verts->data, sizeof(GLubyte) * GWN_vertbuf_size_get(verts));
 
-static void VertexBuffer_unmap(Gwn_VertBuf* verts)
-	{
-	glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
-	glUnmapBuffer(GL_ARRAY_BUFFER);
-	verts->data = NULL;
+#if VRAM_USAGE
+	vbo_memory_usage += GWN_vertbuf_size_get(verts);
+#endif
 	}
 
 void GWN_vertbuf_attr_set(Gwn_VertBuf* verts, unsigned a_idx, unsigned v_idx, const void* data)
@@ -174,11 +146,10 @@ void GWN_vertbuf_attr_set(Gwn_VertBuf* verts, unsigned a_idx, unsigned v_idx, co
 #if TRUST_NO_ONE
 	assert(a_idx < format->attrib_ct);
 	assert(v_idx < verts->vertex_ct);
+	assert(verts->data != NULL);
 #endif
 
-	if (verts->data == NULL)
-		VertexBuffer_map(verts);
-
+	verts->dirty = true;
 	memcpy((GLubyte*)verts->data + a->offset + v_idx * format->stride, data, a->sz);
 	}
 
@@ -203,13 +174,12 @@ void GWN_vertbuf_attr_fill_stride(Gwn_VertBuf* verts, unsigned a_idx, unsigned s
 
 #if TRUST_NO_ONE
 	assert(a_idx < format->attrib_ct);
+	assert(verts->data != NULL);
 #endif
 
+	verts->dirty = true;
 	const unsigned vertex_ct = verts->vertex_ct;
 
-	if (verts->data == NULL)
-		VertexBuffer_map(verts);
-
 	if (format->attrib_ct == 1 && stride == format->stride)
 		{
 		// we can copy it all at once
@@ -230,10 +200,10 @@ void GWN_vertbuf_attr_get_raw_data(Gwn_VertBuf* verts, unsigned a_idx, Gwn_VertB
 
 #if TRUST_NO_ONE
 	assert(a_idx < format->attrib_ct);
+	assert(verts->data != NULL);
 #endif
 
-	if (verts->data == NULL)
-		VertexBuffer_map(verts);
+	verts->dirty = true;
 
 	access->size = a->sz;
 	access->stride = format->stride;
@@ -244,13 +214,30 @@ void GWN_vertbuf_attr_get_raw_data(Gwn_VertBuf* verts, unsigned a_idx, Gwn_VertB
 #endif
 	}
 
+static void VertBuffer_upload_data(Gwn_VertBuf* verts)
+	{
+	unsigned buffer_sz = GWN_vertbuf_size_get(verts);
+
+	// orphan the vbo to avoid sync
+	glBufferData(GL_ARRAY_BUFFER, buffer_sz, NULL, convert_usage_type_to_gl(verts->usage));
+	// upload data
+	glBufferSubData(GL_ARRAY_BUFFER, 0, buffer_sz, verts->data);
+
+	if (verts->usage == GWN_USAGE_STATIC)
+		{
+		free(verts->data);
+		verts->data = NULL;
+		}
+
+	verts->dirty = false;
+	}
+
 void GWN_vertbuf_use(Gwn_VertBuf* verts)
 	{
-	if (verts->data)
-		// this also calls glBindBuffer
-		VertexBuffer_unmap(verts);
-	else
-		glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
+	glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
+
+	if (verts->dirty)
+		VertBuffer_upload_data(verts);
 	}
 
 unsigned GWN_vertbuf_get_memory_usage(void)



More information about the Bf-blender-cvs mailing list