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. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to