--- /dev/null
+#include <algorithm>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_instanced_arrays.h>
+#include <msp/gl/extensions/arb_vertex_array_object.h>
+#include <msp/gl/extensions/arb_vertex_attrib_binding.h>
+#include <msp/gl/extensions/arb_vertex_buffer_object.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include <msp/gl/extensions/ext_gpu_shader4.h>
+#include <msp/gl/extensions/khr_debug.h>
+#include "buffer.h"
+#include "vertexarray.h"
+#include "vertexformat.h"
+#include "vertexsetup.h"
+#include "vertexsetup_backend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OpenGLVertexSetup::OpenGLVertexSetup()
+{
+ static Require req(ARB_vertex_array_object);
+ if(ARB_direct_state_access)
+ glCreateVertexArrays(1, &id);
+ else
+ glGenVertexArrays(1, &id);
+}
+
+OpenGLVertexSetup::~OpenGLVertexSetup()
+{
+ glDeleteVertexArrays(1, &id);
+}
+
+void OpenGLVertexSetup::require_format(const VertexFormat &fmt, bool instanced)
+{
+ if(any_of(fmt.begin(), fmt.end(), is_integer_attribute))
+ static Require _req(EXT_gpu_shader4);
+ if(instanced)
+ static Require req(ARB_instanced_arrays);
+}
+
+void OpenGLVertexSetup::update(unsigned mask) const
+{
+ static bool direct = ARB_direct_state_access && ARB_vertex_attrib_binding;
+
+ if(mask&VertexSetup::VERTEX_ARRAY)
+ update_vertex_array(*static_cast<const VertexSetup *>(this)->vertex_array, 0, 0, direct);
+
+ if(mask&VertexSetup::INSTANCE_ARRAY)
+ update_vertex_array(*static_cast<const VertexSetup *>(this)->inst_array, 1, 1, direct);
+
+ if(mask&VertexSetup::INDEX_BUFFER)
+ {
+ unsigned buf_id = static_cast<const VertexSetup *>(this)->index_buffer->id;
+ if(direct)
+ glVertexArrayElementBuffer(id, buf_id);
+ else
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf_id);
+ }
+}
+
+void OpenGLVertexSetup::update_vertex_array(const VertexArray &array, unsigned binding, unsigned divisor, bool direct) const
+{
+ if(!direct)
+ {
+ OpenGLBuffer::unbind_scratch();
+ glBindBuffer(GL_ARRAY_BUFFER, array.get_buffer()->id);
+ }
+
+ const VertexFormat &fmt = array.get_format();
+ unsigned stride = fmt.stride();
+ if(direct)
+ {
+ glVertexArrayVertexBuffer(id, binding, array.get_buffer()->id, 0, stride);
+ glVertexArrayBindingDivisor(id, binding, divisor);
+ }
+
+ unsigned offset = 0;
+ for(VertexAttribute a: fmt)
+ {
+ unsigned sem = get_attribute_semantic(a);
+ bool integer = is_integer_attribute(a);
+ GLenum type = get_gl_type(get_attribute_source_type(a));
+ unsigned cc = get_attribute_component_count(a);
+ if(direct)
+ {
+ if(integer)
+ glVertexArrayAttribIFormat(id, sem, cc, type, offset);
+ else
+ glVertexArrayAttribFormat(id, sem, cc, type, true, offset);
+ glVertexArrayAttribBinding(id, sem, binding);
+ glEnableVertexArrayAttrib(id, sem);
+ }
+ else
+ {
+ if(integer)
+ glVertexAttribIPointer(sem, cc, type, stride, reinterpret_cast<void *>(offset));
+ else
+ glVertexAttribPointer(sem, cc, type, true, stride, reinterpret_cast<void *>(offset));
+ if(ARB_instanced_arrays)
+ glVertexAttribDivisor(sem, divisor);
+ glEnableVertexAttribArray(sem);
+ }
+ offset += get_attribute_size(a);
+ }
+
+ if(!direct)
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void OpenGLVertexSetup::unload()
+{
+ if(ARB_direct_state_access)
+ {
+ glVertexArrayVertexBuffer(id, 0, 0, 0, 0);
+ glVertexArrayVertexBuffer(id, 1, 0, 0, 0);
+ glVertexArrayElementBuffer(id, 0);
+ }
+ else
+ {
+ glBindVertexArray(id);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ for(VertexAttribute a: static_cast<const VertexSetup *>(this)->vertex_format)
+ {
+ unsigned sem = get_attribute_semantic(a);
+ glDisableVertexAttribArray(sem);
+ glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0);
+ }
+ for(VertexAttribute a: static_cast<const VertexSetup *>(this)->inst_format)
+ {
+ unsigned sem = get_attribute_semantic(a);
+ glDisableVertexAttribArray(sem);
+ glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0);
+ }
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+}
+
+void OpenGLVertexSetup::set_debug_name(const string &name)
+{
+#ifdef DEBUG
+ if(KHR_debug)
+ glObjectLabel(GL_VERTEX_ARRAY, id, name.size(), name.c_str());
+#else
+ (void)name;
+#endif
+}
+
+} // namespace GL
+} // namespace Msp