[Bf-blender-cvs] [797f189] blender2.8: OpenGL: immediate mode work-alike

Mike Erwin noreply at git.blender.org
Thu Aug 4 22:06:29 CEST 2016


Commit: 797f1896fa023c0404965b987a7334448857c665
Author: Mike Erwin
Date:   Thu Aug 4 15:36:20 2016 -0400
Branches: blender2.8
https://developer.blender.org/rB797f1896fa023c0404965b987a7334448857c665

OpenGL: immediate mode work-alike

Introducing an immediate mode drawing API that works with modern GL 3.2
core profile. I wrote and tested this using a core context on Mac.

This is part of the Gawain library which is Apache 2 licensed. Be very
careful not to pull other Blender code into these files.

Modifications for the Blender integration:
- prefix filenames to match rest of Blender’s GPU libs
- include GPU_glew.h instead of <OpenGL/gl3.h>
- disable thread-local vars until we figure out how best to do this

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

M	source/blender/gpu/CMakeLists.txt
A	source/blender/gpu/GPU_immediate.h
A	source/blender/gpu/intern/gpu_immediate.c

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

diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index cfa0831..5708352 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -54,6 +54,7 @@ set(SRC
 	intern/gpu_draw.c
 	intern/gpu_extensions.c
 	intern/gpu_framebuffer.c
+	intern/gpu_immediate.c
 	intern/gpu_init_exit.c
 	intern/gpu_material.c
 	intern/gpu_select.c
@@ -89,6 +90,7 @@ set(SRC
 	GPU_extensions.h
 	GPU_framebuffer.h
 	GPU_glew.h
+	GPU_immediate.h
 	GPU_init_exit.h
 	GPU_material.h
 	GPU_select.h
diff --git a/source/blender/gpu/GPU_immediate.h b/source/blender/gpu/GPU_immediate.h
new file mode 100644
index 0000000..aeed133
--- /dev/null
+++ b/source/blender/gpu/GPU_immediate.h
@@ -0,0 +1,67 @@
+
+// Gawain immediate mode work-alike, take 2
+//
+// This code is part of the Gawain library, with modifications
+// specific to integration with Blender.
+//
+// Copyright 2016 Mike Erwin
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+#pragma once
+
+#include "GPU_glew.h"
+#include <stdbool.h>
+
+#define PER_THREAD
+// #define PER_THREAD __thread
+// MSVC uses __declspec(thread) for C code
+
+#define MAX_VERTEX_ATTRIBS 16
+
+#define TRUST_NO_ONE 1
+
+typedef enum {
+	KEEP_FLOAT,
+	KEEP_INT,
+	NORMALIZE_INT_TO_FLOAT, // 127 (ubyte) -> 0.5 (and so on for other int types)
+	CONVERT_INT_TO_FLOAT // 127 (any int type) -> 127.0
+} VertexFetchMode;
+
+typedef struct {
+	GLenum comp_type;
+	unsigned comp_ct; // 1 to 4
+	unsigned sz; // size in bytes, 1 to 16
+	unsigned offset; // from beginning of vertex, in bytes
+	VertexFetchMode fetch_mode;
+	char* name; // TODO: shared allocation of all names within a VertexFormat
+} Attrib;
+
+typedef struct {
+	unsigned attrib_ct; // 0 to 16 (MAX_VERTEX_ATTRIBS)
+	unsigned stride; // stride in bytes, 1 to 256
+	bool packed;
+	Attrib attribs[MAX_VERTEX_ATTRIBS]; // TODO: variable-size attribs array
+} VertexFormat;
+
+void clear_VertexFormat(VertexFormat*);
+unsigned add_attrib(VertexFormat*, const char* name, GLenum comp_type, unsigned comp_ct, VertexFetchMode);
+void pack(VertexFormat*);
+// unsigned attrib_idx(const VertexFormat*, const char* name);
+void bind_attrib_locations(const VertexFormat*, GLuint program);
+
+extern PER_THREAD VertexFormat immVertexFormat; // so we don't have to copy or pass around
+
+void immInit(void);
+void immDestroy(void);
+
+void immBegin(GLenum primitive, unsigned vertex_ct);
+void immEnd(void);
+
+void immAttrib1f(unsigned attrib_id, float x);
+void immAttrib3f(unsigned attrib_id, float x, float y, float z);
+
+void immEndVertex(void); // and move on to the next vertex
diff --git a/source/blender/gpu/intern/gpu_immediate.c b/source/blender/gpu/intern/gpu_immediate.c
new file mode 100644
index 0000000..e9d19ae
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_immediate.c
@@ -0,0 +1,417 @@
+
+// Gawain immediate mode work-alike, take 2
+//
+// This code is part of the Gawain library, with modifications
+// specific to integration with Blender.
+//
+// Copyright 2016 Mike Erwin
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+#include "GPU_immediate.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PACK_DEBUG 0
+
+#if PACK_DEBUG
+  #include <stdio.h>
+#endif
+
+void clear_VertexFormat(VertexFormat* format)
+	{
+	for (unsigned a = 0; a < format->attrib_ct; ++a)
+		free(format->attribs[a].name);
+
+#if TRUST_NO_ONE
+	memset(format, 0, sizeof(VertexFormat));
+#else
+	format->attrib_ct = 0;
+	format->packed = false;
+#endif
+	}
+
+static unsigned comp_sz(GLenum type)
+	{
+#if TRUST_NO_ONE
+	assert(type >= GL_BYTE && type <= GL_FLOAT);
+#endif
+
+	const GLubyte sizes[] = {1,1,2,2,4,4,4};
+	return sizes[type - GL_BYTE];
+	}
+
+static unsigned attrib_sz(const Attrib *a)
+	{
+	return a->comp_ct * comp_sz(a->comp_type);
+	}
+
+static unsigned attrib_align(const Attrib *a)
+	{
+	unsigned c = comp_sz(a->comp_type);
+	if (a->comp_ct == 3 && c <= 2)
+		return 4 * c; // AMD HW can't fetch these well, so pad it out (other vendors too?)
+	else
+		return c; // most fetches are ok if components are naturally aligned
+	}
+
+static unsigned vertex_buffer_size(const VertexFormat* format, unsigned vertex_ct)
+	{
+#if TRUST_NO_ONE
+	assert(format->packed && format->stride > 0);
+#endif
+
+	return format->stride * vertex_ct;
+	}
+
+unsigned add_attrib(VertexFormat* format, const char* name, GLenum comp_type, unsigned comp_ct, VertexFetchMode fetch_mode)
+	{
+#if TRUST_NO_ONE
+	assert(format->attrib_ct < MAX_VERTEX_ATTRIBS); // there's room for more
+	assert(!format->packed); // packed means frozen/locked
+#endif
+
+	const unsigned attrib_id = format->attrib_ct++;
+	Attrib* attrib = format->attribs + attrib_id;
+
+	attrib->name = strdup(name);
+	attrib->comp_type = comp_type;
+	attrib->comp_ct = comp_ct;
+	attrib->sz = attrib_sz(attrib);
+	attrib->offset = 0; // offsets & stride are calculated later (during pack)
+	attrib->fetch_mode = fetch_mode;
+
+	return attrib_id;
+	}
+
+static unsigned padding(unsigned offset, unsigned alignment)
+	{
+	const unsigned mod = offset % alignment;
+	return (mod == 0) ? 0 : (alignment - mod);
+	}
+
+#if PACK_DEBUG
+void show_pack(a_idx, sz, pad)
+	{
+	const char c = 'A' + a_idx;
+	for (unsigned i = 0; i < pad; ++i)
+		putchar('-');
+	for (unsigned i = 0; i < sz; ++i)
+		putchar(c);
+	}
+#endif
+
+void pack(VertexFormat* format)
+	{
+	// for now, attributes are packed in the order they were added,
+	// making sure each attrib is naturally aligned (add padding where necessary)
+
+	// later we can implement more efficient packing w/ reordering
+
+	Attrib* a0 = format->attribs + 0;
+	a0->offset = 0;
+	unsigned offset = a0->sz;
+
+#if PACK_DEBUG
+	show_pack(0, a0->sz, 0);
+#endif
+
+	for (unsigned a_idx = 1; a_idx < format->attrib_ct; ++a_idx)
+		{
+		Attrib* a = format->attribs + a_idx;
+		unsigned mid_padding = padding(offset, attrib_align(a));
+		offset += mid_padding;
+		a->offset = offset;
+		offset += a->sz;
+
+#if PACK_DEBUG
+		show_pack(a_idx, a->sz, mid_padding);
+#endif
+		}
+
+	unsigned end_padding = padding(offset, attrib_align(a0));
+
+#if PACK_DEBUG
+	show_pack(0, 0, end_padding);
+	putchar('\n');
+#endif
+
+	format->stride = offset + end_padding;
+	format->packed = true;
+	}
+
+void bind_attrib_locations(const VertexFormat* format, GLuint program)
+	{
+#if TRUST_NO_ONE
+	assert(glIsProgram(program));
+#endif
+
+	for (unsigned a_idx = 0; a_idx < format->attrib_ct; ++a_idx)
+		{
+		const Attrib* a = &format->attribs[a_idx];
+		glBindAttribLocation(program, a_idx, a->name);
+		}
+	}
+
+// --- immediate mode work-alike --------------------------------
+
+typedef struct {
+	// current draw call
+	void* buffer_data;
+	unsigned buffer_offset;
+	unsigned buffer_bytes_mapped;
+	unsigned vertex_ct;
+	GLenum primitive;
+
+	// current vertex
+	unsigned vertex_idx;
+	void* vertex_data;
+	unsigned short attrib_value_bits; // which attributes of current vertex have been given values?
+
+	GLuint vbo_id;
+	GLuint vao_id;
+} Immediate;
+
+// size of internal buffer -- make this adjustable?
+// #define IMM_BUFFER_SIZE (4 * 1024 * 1024)
+#define IMM_BUFFER_SIZE 1024
+
+static PER_THREAD bool initialized = false;
+static PER_THREAD Immediate imm;
+PER_THREAD VertexFormat immVertexFormat;
+
+void immInit()
+	{
+#if TRUST_NO_ONE
+	assert(!initialized);
+#endif
+
+	clear_VertexFormat(&immVertexFormat);
+	memset(&imm, 0, sizeof(Immediate));
+
+	glGenVertexArrays(1, &imm.vao_id);
+	glBindVertexArray(imm.vao_id);
+	glGenBuffers(1, &imm.vbo_id);
+	glBindBuffer(GL_ARRAY_BUFFER, imm.vbo_id);
+	glBufferData(GL_ARRAY_BUFFER, IMM_BUFFER_SIZE, NULL, GL_DYNAMIC_DRAW);
+
+	imm.primitive = GL_NONE;
+	
+	// glBindBuffer(GL_ARRAY_BUFFER, 0);
+	initialized = true;
+	}
+
+void immDestroy()
+	{
+#if TRUST_NO_ONE
+	assert(initialized);
+	assert(imm.primitive == GL_NONE); // make sure we're not between a Begin/End pair
+#endif
+
+	clear_VertexFormat(&immVertexFormat);
+	glDeleteVertexArrays(1, &imm.vao_id);
+	glDeleteBuffers(1, &imm.vbo_id);
+	initialized = false;
+	}
+
+void immBegin(GLenum primitive, unsigned vertex_ct)
+	{
+#if TRUST_NO_ONE
+	assert(initialized);
+	assert(imm.primitive == GL_NONE); // make sure we haven't already begun
+
+	// does vertex_ct make sense for this primitive type?
+	assert(vertex_ct > 0);
+	switch (primitive)
+		{
+		case GL_POINTS:
+			break;
+		case GL_LINES:
+			assert(vertex_ct % 2 == 0);
+			break;
+		case GL_LINE_STRIP:
+		case GL_LINE_LOOP:
+			assert(vertex_ct > 2); // otherwise why bother?
+			break;
+		case GL_TRIANGLES:
+			assert(vertex_ct % 3 == 0);
+			break;
+		default:
+			assert(false);
+		}
+#endif
+
+	imm.primitive = primitive;
+	imm.vertex_ct = vertex_ct;
+	imm.vertex_idx = 0;
+	imm.attrib_value_bits = 0;
+
+	// how many bytes do we need for this draw call?
+	const unsigned bytes_needed = vertex_buffer_size(&immVertexFormat, vertex_ct);
+
+#if TRUST_NO_ONE
+	assert(bytes_needed <= IMM_BUFFER_SIZE);
+#endif
+
+//	glBindBuffer(GL_ARRAY_BUFFER, imm.vbo_id);
+
+	// does the current buffer have enough room?
+	const unsigned available_bytes = IMM_BUFFER_SIZE - imm.buffer_offset;
+	// ensure vertex data is aligned
+	const unsigned pre_padding = padding(imm.buffer_offset, immVertexFormat.stride); // might waste a little space, but it's safe
+	if ((bytes_needed + pre_padding) <= available_bytes)
+		imm.buffer_offset += pre_padding;
+	else
+		{
+		// orphan this buffer & start with a fresh one
+		glMapBufferRange(GL_ARRAY_BUFFER, 0, IMM_BUFFER_SIZE, GL_MAP_INVALIDATE_BUFFER_BIT);
+		// glInvalidateBufferData(imm.vbo_id); // VERSION >= 4.3 || ARB_invalidate_subdata
+
+		imm.buffer_offset = 0;
+		}
+
+//	printf("mapping %u to %u\n", imm.buffer_offset, imm.buffer_offset + bytes_needed - 1);
+
+	imm.buffer_data = glMapBufferRange(GL_ARRAY_BUFFER, imm.buffer_offset, bytes_needed, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
+
+#if TRUST_NO_ONE
+	asser

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list