Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2020-08-11 02:31:40 +0300
committerClément Foucault <foucault.clem@gmail.com>2020-08-13 15:20:24 +0300
commitefc97b3919ea4fd46b9d2e931ca3fea27e7ea31c (patch)
tree125e4239c90902a967487f44d5aca1696d49be68 /source/blender/gpu/opengl
parent47bfb0f7ad2f70017585fe55a68e49ae09f1150c (diff)
GPUBatch: GL backend isolation
This changes the drawing paradigm a bit. The VAO configuration is done JIT-style and depends on context active shader. This is to allow more flexibility for implementations to do optimization at lower level. The vao cache is now its own class to isolate the concept. It is this class that is reference by the GLContext for ownership of the containing VAO ids.
Diffstat (limited to 'source/blender/gpu/opengl')
-rw-r--r--source/blender/gpu/opengl/gl_batch.cc321
-rw-r--r--source/blender/gpu/opengl/gl_batch.hh49
-rw-r--r--source/blender/gpu/opengl/gl_context.cc15
-rw-r--r--source/blender/gpu/opengl/gl_context.hh13
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.cc158
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.hh44
6 files changed, 570 insertions, 30 deletions
diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc
index 62d81ad9f5a..00e1a61f7cf 100644
--- a/source/blender/gpu/opengl/gl_batch.cc
+++ b/source/blender/gpu/opengl/gl_batch.cc
@@ -29,13 +29,254 @@
#include "glew-mx.h"
+#include "GPU_extensions.h"
+
#include "gpu_batch_private.hh"
#include "gpu_primitive_private.h"
+#include "gpu_shader_private.h"
#include "gl_batch.hh"
+#include "gl_context.hh"
+#include "gl_vertex_array.hh"
using namespace blender::gpu;
+/* -------------------------------------------------------------------- */
+/** \name Vao cache
+ *
+ * Each GLBatch has a small cache of VAO objects that are used to avoid VAO reconfiguration.
+ * TODO(fclem) Could be revisited to avoid so much cross references.
+ * \{ */
+
+GLVaoCache::GLVaoCache(void)
+{
+ init();
+}
+
+GLVaoCache::~GLVaoCache()
+{
+ this->clear();
+}
+
+void GLVaoCache::init(void)
+{
+ context_ = NULL;
+ interface_ = NULL;
+ is_dynamic_vao_count = false;
+ for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) {
+ static_vaos.interfaces[i] = NULL;
+ static_vaos.vao_ids[i] = 0;
+ }
+ vao_base_instance_ = 0;
+ base_instance_ = 0;
+}
+
+/* Create a new VAO object and store it in the cache. */
+void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao)
+{
+ /* Now insert the cache. */
+ if (!is_dynamic_vao_count) {
+ int i; /* find first unused slot */
+ for (i = 0; i < GPU_VAO_STATIC_LEN; i++) {
+ if (static_vaos.vao_ids[i] == 0) {
+ break;
+ }
+ }
+
+ if (i < GPU_VAO_STATIC_LEN) {
+ static_vaos.interfaces[i] = interface;
+ static_vaos.vao_ids[i] = vao;
+ }
+ else {
+ /* Erase previous entries, they will be added back if drawn again. */
+ for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) {
+ if (static_vaos.interfaces[i] != NULL) {
+ GPU_shaderinterface_remove_batch_ref(
+ const_cast<GPUShaderInterface *>(static_vaos.interfaces[i]), this);
+ context_->vao_free(static_vaos.vao_ids[i]);
+ }
+ }
+ /* Not enough place switch to dynamic. */
+ is_dynamic_vao_count = true;
+ /* Init dynamic arrays and let the branch below set the values. */
+ dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT;
+ dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN(
+ dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces");
+ dynamic_vaos.vao_ids = (GLuint *)MEM_callocN(dynamic_vaos.count * sizeof(GLuint),
+ "dyn vaos ids");
+ }
+ }
+
+ if (is_dynamic_vao_count) {
+ int i; /* find first unused slot */
+ for (i = 0; i < dynamic_vaos.count; i++) {
+ if (dynamic_vaos.vao_ids[i] == 0) {
+ break;
+ }
+ }
+
+ if (i == dynamic_vaos.count) {
+ /* Not enough place, realloc the array. */
+ i = dynamic_vaos.count;
+ dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT;
+ dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN(
+ (void *)dynamic_vaos.interfaces, sizeof(GPUShaderInterface *) * dynamic_vaos.count);
+ dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN(dynamic_vaos.vao_ids,
+ sizeof(GLuint) * dynamic_vaos.count);
+ }
+ dynamic_vaos.interfaces[i] = interface;
+ dynamic_vaos.vao_ids[i] = vao;
+ }
+
+ GPU_shaderinterface_add_batch_ref(const_cast<GPUShaderInterface *>(interface), this);
+}
+
+void GLVaoCache::remove(const GPUShaderInterface *interface)
+{
+ const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
+ GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids;
+ const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
+ static_vaos.interfaces;
+ for (int i = 0; i < count; i++) {
+ if (interfaces[i] == interface) {
+ context_->vao_free(vaos[i]);
+ vaos[i] = 0;
+ interfaces[i] = NULL;
+ break; /* cannot have duplicates */
+ }
+ }
+}
+
+void GLVaoCache::clear(void)
+{
+ GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get());
+ const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
+ GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids;
+ const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
+ static_vaos.interfaces;
+ /* Early out, nothing to free. */
+ if (context_ == NULL) {
+ return;
+ }
+
+ if (context_ == ctx) {
+ glDeleteVertexArrays(count, vaos);
+ glDeleteVertexArrays(1, &vao_base_instance_);
+ }
+ else {
+ /* TODO(fclem) Slow way. Could avoid multiple mutex lock here */
+ for (int i = 0; i < count; i++) {
+ context_->vao_free(vaos[i]);
+ }
+ context_->vao_free(vao_base_instance_);
+ }
+
+ for (int i = 0; i < count; i++) {
+ if (interfaces[i] == NULL) {
+ continue;
+ }
+ GPU_shaderinterface_remove_batch_ref(const_cast<GPUShaderInterface *>(interfaces[i]), this);
+ }
+
+ if (is_dynamic_vao_count) {
+ MEM_freeN((void *)dynamic_vaos.interfaces);
+ MEM_freeN(dynamic_vaos.vao_ids);
+ }
+
+ if (context_) {
+ context_->vao_cache_unregister(this);
+ }
+ /* Reinit. */
+ this->init();
+}
+
+/* Return 0 on cache miss (invalid VAO) */
+GLuint GLVaoCache::lookup(const GPUShaderInterface *interface)
+{
+ const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
+ const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
+ static_vaos.interfaces;
+ for (int i = 0; i < count; i++) {
+ if (interfaces[i] == interface) {
+ return (is_dynamic_vao_count) ? dynamic_vaos.vao_ids[i] : static_vaos.vao_ids[i];
+ }
+ }
+ return 0;
+}
+
+/* The GLVaoCache object is only valid for one GLContext.
+ * Reset the cache if trying to draw in another context; */
+void GLVaoCache::context_check(void)
+{
+ GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get());
+ BLI_assert(ctx);
+
+ if (context_ != ctx) {
+ if (context_ != NULL) {
+ /* IMPORTANT: Trying to draw a batch in multiple different context will trash the VAO cache.
+ * This has major performance impact and should be avoided in most cases. */
+ context_->vao_cache_unregister(this);
+ }
+ this->clear();
+ context_ = ctx;
+ context_->vao_cache_register(this);
+ }
+}
+
+GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first)
+{
+ this->context_check();
+ /* Make sure the interface is up to date. */
+ if (interface_ != GPU_context_active_get()->shader->interface) {
+ vao_get(batch);
+ /* Trigger update. */
+ base_instance_ = 0;
+ }
+ /**
+ * There seems to be a nasty bug when drawing using the same VAO reconfiguring (T71147).
+ * We just use a throwaway VAO for that. Note that this is likely to degrade performance.
+ **/
+#ifdef __APPLE__
+ glDeleteVertexArrays(1, &vao_base_instance_);
+ vao_base_instance_ = 0;
+#endif
+
+ if (vao_base_instance_ == 0) {
+ glGenVertexArrays(1, &vao_base_instance_);
+ }
+
+ if (base_instance_ != i_first) {
+ base_instance_ = i_first;
+ GLVertArray::update_bindings(vao_base_instance_, batch, interface_, i_first);
+ }
+ return base_instance_;
+}
+
+GLuint GLVaoCache::vao_get(GPUBatch *batch)
+{
+ this->context_check();
+
+ GPUContext *ctx = GPU_context_active_get();
+ if (interface_ != ctx->shader->interface) {
+ interface_ = ctx->shader->interface;
+ vao_id_ = this->lookup(interface_);
+
+ if (vao_id_ == 0) {
+ /* Cache miss, create a new VAO. */
+ glGenVertexArrays(1, &vao_id_);
+ this->insert(interface_, vao_id_);
+ GLVertArray::update_bindings(vao_id_, batch, interface_, 0);
+ }
+ }
+
+ return vao_id_;
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Creation & Deletion
+ * \{ */
+
GLBatch::GLBatch(void)
{
}
@@ -44,7 +285,83 @@ GLBatch::~GLBatch()
{
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Drawing
+ * \{ */
+
+#if GPU_TRACK_INDEX_RANGE
+# define BASE_INDEX(el) ((el)->base_index)
+# define INDEX_TYPE(el) ((el)->gl_index_type)
+#else
+# define BASE_INDEX(el) 0
+# define INDEX_TYPE(el) GL_UNSIGNED_INT
+#endif
+
+void GLBatch::bind(int i_first)
+{
+ if (flag & GPU_BATCH_DIRTY) {
+ vao_cache_.clear();
+ }
+
+#if GPU_TRACK_INDEX_RANGE
+ /* Can be removed if GL 4.3 is required. */
+ if (!GLEW_ARB_ES3_compatibility && (elem != NULL)) {
+ glPrimitiveRestartIndex((elem->index_type == GPU_INDEX_U16) ? 0xFFFFu : 0xFFFFFFFFu);
+ }
+#endif
+
+ /* Can be removed if GL 4.2 is required. */
+ if (!GPU_arb_base_instance_is_supported() && (i_first > 0)) {
+ glBindVertexArray(vao_cache_.base_instance_vao_get(this, i_first));
+ }
+ else {
+ glBindVertexArray(vao_cache_.vao_get(this));
+ }
+}
+
void GLBatch::draw(int v_first, int v_count, int i_first, int i_count)
{
- UNUSED_VARS(v_first, v_count, i_first, i_count);
-} \ No newline at end of file
+ this->bind(i_first);
+
+ GLenum gl_type = convert_prim_type_to_gl(prim_type);
+
+ if (elem) {
+ const GPUIndexBuf *el = elem;
+ GLenum index_type = INDEX_TYPE(el);
+ GLint base_index = BASE_INDEX(el);
+ void *v_first_ofs = (GLuint *)0 + v_first + el->index_start;
+
+#if GPU_TRACK_INDEX_RANGE
+ if (el->index_type == GPU_INDEX_U16) {
+ v_first_ofs = (GLushort *)0 + v_first + el->index_start;
+ }
+#endif
+
+ if (GPU_arb_base_instance_is_supported()) {
+ glDrawElementsInstancedBaseVertexBaseInstance(
+ gl_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first);
+ }
+ else {
+ glDrawElementsInstancedBaseVertex(
+ gl_type, v_count, index_type, v_first_ofs, i_count, base_index);
+ }
+ }
+ else {
+#ifdef __APPLE__
+ glDisable(GL_PRIMITIVE_RESTART);
+#endif
+ if (GPU_arb_base_instance_is_supported()) {
+ glDrawArraysInstancedBaseInstance(gl_type, v_first, v_count, i_count, i_first);
+ }
+ else {
+ glDrawArraysInstanced(gl_type, v_first, v_count, i_count);
+ }
+#ifdef __APPLE__
+ glEnable(GL_PRIMITIVE_RESTART);
+#endif
+ }
+}
+
+/** \} */
diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh
index 290c113205a..490c9180a99 100644
--- a/source/blender/gpu/opengl/gl_batch.hh
+++ b/source/blender/gpu/opengl/gl_batch.hh
@@ -37,18 +37,29 @@
namespace blender {
namespace gpu {
-#define GPU_BATCH_VAO_STATIC_LEN 3
+#define GPU_VAO_STATIC_LEN 3
+/* Vao management: remembers all geometry state (vertex attribute bindings & element buffer)
+ * for each shader interface. Start with a static number of vaos and fallback to dynamic count
+ * if necessary. Once a batch goes dynamic it does not go back. */
class GLVaoCache {
- /* Vao management: remembers all geometry state (vertex attribute bindings & element buffer)
- * for each shader interface. Start with a static number of vaos and fallback to dynamic count
- * if necessary. Once a batch goes dynamic it does not go back. */
+ private:
+ /** Context for which the vao_cache_ was generated. */
+ struct GLContext *context_ = NULL;
+ /** Last interface this batch was drawn with. */
+ GPUShaderInterface *interface_ = NULL;
+ /** Cached vao for the last interface. */
+ GLuint vao_id_ = 0;
+ /** Used whend arb_base_instance is not supported. */
+ GLuint vao_base_instance_ = 0;
+ int base_instance_ = 0;
+
bool is_dynamic_vao_count = false;
union {
/** Static handle count */
struct {
- const GPUShaderInterface *interfaces[GPU_BATCH_VAO_STATIC_LEN];
- GLuint vao_ids[GPU_BATCH_VAO_STATIC_LEN];
+ const GPUShaderInterface *interfaces[GPU_VAO_STATIC_LEN];
+ GLuint vao_ids[GPU_VAO_STATIC_LEN];
} static_vaos;
/** Dynamic handle count */
struct {
@@ -58,18 +69,27 @@ class GLVaoCache {
} dynamic_vaos;
};
- GLuint search(const GPUShaderInterface *interface);
- void insert(GLuint vao_id, const GPUShaderInterface *interface);
+ public:
+ GLVaoCache();
+ ~GLVaoCache();
+
+ GLuint vao_get(GPUBatch *batch);
+ GLuint base_instance_vao_get(GPUBatch *batch, int i_first);
+
+ GLuint lookup(const GPUShaderInterface *interface);
+ void insert(const GPUShaderInterface *interface, GLuint vao_id);
+ void remove(const GPUShaderInterface *interface);
void clear(void);
- void interface_remove(const GPUShaderInterface *interface);
+
+ private:
+ void init(void);
+ void context_check(void);
};
class GLBatch : public Batch {
- private:
- /** Cached values (avoid dereferencing later). */
- GLuint vao_id;
+ public:
/** All vaos corresponding to all the GPUShaderInterface this batch was drawn with. */
- GLVaoCache vaos;
+ GLVaoCache vao_cache_;
public:
GLBatch();
@@ -77,6 +97,9 @@ class GLBatch : public Batch {
void draw(int v_first, int v_count, int i_first, int i_count) override;
+ private:
+ void bind(int i_first);
+
MEM_CXX_CLASS_ALLOC_FUNCS("GLBatch");
};
diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc
index 00a10924ff6..dd413612879 100644
--- a/source/blender/gpu/opengl/gl_context.cc
+++ b/source/blender/gpu/opengl/gl_context.cc
@@ -63,8 +63,8 @@ GLContext::~GLContext()
/* For now don't allow GPUFrameBuffers to be reuse in another context. */
BLI_assert(framebuffers_.is_empty());
/* Delete vaos so the batch can be reused in another context. */
- for (GPUBatch *batch : batches_) {
- GPU_batch_vao_cache_clear(batch);
+ for (GLVaoCache *cache : vao_caches_) {
+ cache->clear();
}
glDeleteVertexArrays(1, &default_vao_);
glDeleteBuffers(1, &default_attr_vbo_);
@@ -197,20 +197,17 @@ void GLBackend::tex_free(GLuint tex_id)
* is discarded.
* \{ */
-void GLContext::batch_register(struct GPUBatch *batch)
+void GLContext::vao_cache_register(GLVaoCache *cache)
{
lists_mutex_.lock();
- batches_.add(batch);
+ vao_caches_.add(cache);
lists_mutex_.unlock();
}
-void GLContext::batch_unregister(struct GPUBatch *batch)
+void GLContext::vao_cache_unregister(GLVaoCache *cache)
{
- /* vao_cache_clear() can acquire lists_mutex_ so avoid deadlock. */
- // reinterpret_cast<GLBatch *>(batch)->vao_cache_clear();
-
lists_mutex_.lock();
- batches_.remove(batch);
+ vao_caches_.remove(cache);
lists_mutex_.unlock();
}
diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh
index 3b55965b9d1..f3ff5cb47f4 100644
--- a/source/blender/gpu/opengl/gl_context.hh
+++ b/source/blender/gpu/opengl/gl_context.hh
@@ -25,15 +25,16 @@
#include "gpu_context_private.hh"
+#include "GPU_framebuffer.h"
+
#include "BLI_set.hh"
#include "BLI_vector.hh"
#include "glew-mx.h"
-#include <iostream>
+#include "gl_batch.hh"
+
#include <mutex>
-#include <unordered_set>
-#include <vector>
namespace blender {
namespace gpu {
@@ -63,7 +64,7 @@ class GLContext : public GPUContext {
* GPUBatch & GPUFramebuffer have references to the context they are from, in the case the
* context is destroyed, we need to remove any reference to it.
*/
- Set<GPUBatch *> batches_;
+ Set<GLVaoCache *> vao_caches_;
Set<GPUFrameBuffer *> framebuffers_;
/** Mutex for the bellow structures. */
std::mutex lists_mutex_;
@@ -87,8 +88,8 @@ class GLContext : public GPUContext {
void vao_free(GLuint vao_id);
void fbo_free(GLuint fbo_id);
- void batch_register(struct GPUBatch *batch);
- void batch_unregister(struct GPUBatch *batch);
+ void vao_cache_register(GLVaoCache *cache);
+ void vao_cache_unregister(GLVaoCache *cache);
void framebuffer_register(struct GPUFrameBuffer *fb);
void framebuffer_unregister(struct GPUFrameBuffer *fb);
};
diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc
new file mode 100644
index 00000000000..907dc37e46f
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_vertex_array.cc
@@ -0,0 +1,158 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 by Mike Erwin.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "GPU_glew.h"
+
+#include "GPU_shader_interface.h"
+#include "GPU_vertex_buffer.h"
+
+#include "gpu_vertex_format_private.h"
+
+#include "gl_batch.hh"
+#include "gl_context.hh"
+
+#include "gl_vertex_array.hh"
+
+using namespace blender::gpu;
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Array Bindings
+ * \{ */
+
+/* Returns enabled vertex pointers as a bitflag (one bit per attrib). */
+static uint16_t vbo_bind(const GPUShaderInterface *interface,
+ const GPUVertFormat *format,
+ uint v_first,
+ uint v_len,
+ const bool use_instancing)
+{
+ uint16_t enabled_attrib = 0;
+ const uint attr_len = format->attr_len;
+ uint stride = format->stride;
+ uint offset = 0;
+ GLuint divisor = (use_instancing) ? 1 : 0;
+
+ for (uint a_idx = 0; a_idx < attr_len; a_idx++) {
+ const GPUVertAttr *a = &format->attrs[a_idx];
+
+ if (format->deinterleaved) {
+ offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * v_len;
+ stride = a->sz;
+ }
+ else {
+ offset = a->offset;
+ }
+
+ const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride;
+ const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type));
+
+ for (uint n_idx = 0; n_idx < a->name_len; n_idx++) {
+ const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
+ const GPUShaderInput *input = GPU_shaderinterface_attr(interface, name);
+
+ if (input == NULL) {
+ continue;
+ }
+
+ enabled_attrib |= (1 << input->location);
+
+ if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) {
+ BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT);
+ BLI_assert(a->comp_type == GPU_COMP_F32);
+ for (int i = 0; i < a->comp_len / 4; i++) {
+ glEnableVertexAttribArray(input->location + i);
+ glVertexAttribDivisor(input->location + i, divisor);
+ glVertexAttribPointer(
+ input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16);
+ }
+ }
+ else {
+ glEnableVertexAttribArray(input->location);
+ glVertexAttribDivisor(input->location, divisor);
+
+ switch (a->fetch_mode) {
+ case GPU_FETCH_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT:
+ glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, pointer);
+ break;
+ case GPU_FETCH_INT_TO_FLOAT_UNIT:
+ glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer);
+ break;
+ case GPU_FETCH_INT:
+ glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer);
+ break;
+ }
+ }
+ }
+ }
+ return enabled_attrib;
+}
+
+/* Update the Attrib Binding of the currently bound VAO. */
+void GLVertArray::update_bindings(const GLuint vao,
+ const GPUBatch *batch,
+ const GPUShaderInterface *interface,
+ const int base_instance)
+{
+ uint16_t attr_mask = interface->enabled_attr_mask;
+
+ glBindVertexArray(vao);
+
+ /* Reverse order so first VBO'S have more prevalence (in term of attribute override). */
+ for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) {
+ GPUVertBuf *vbo = batch->verts[v];
+ if (vbo) {
+ GPU_vertbuf_use(vbo);
+ attr_mask &= ~vbo_bind(interface, &vbo->format, 0, vbo->vertex_len, false);
+ }
+ }
+
+ for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) {
+ GPUVertBuf *vbo = batch->inst[v];
+ if (vbo) {
+ GPU_vertbuf_use(vbo);
+ attr_mask &= ~vbo_bind(interface, &vbo->format, base_instance, vbo->vertex_len, true);
+ }
+ }
+
+ if (attr_mask != 0 && GLEW_ARB_vertex_attrib_binding) {
+ for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) {
+ if (attr_mask & mask) {
+ GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get());
+ /* This replaces glVertexAttrib4f(a, 0.0f, 0.0f, 0.0f, 1.0f); with a more modern style.
+ * Fix issues for some drivers (see T75069). */
+ glBindVertexBuffer(a, ctx->default_attr_vbo_, (intptr_t)0, (intptr_t)0);
+ glEnableVertexAttribArray(a);
+ glVertexAttribFormat(a, 4, GL_FLOAT, GL_FALSE, 0);
+ glVertexAttribBinding(a, a);
+ }
+ }
+ }
+
+ if (batch->elem) {
+ /* Binds the index buffer. This state is also saved in the VAO. */
+ GPU_indexbuf_use(batch->elem);
+ }
+}
+
+/** \} */
diff --git a/source/blender/gpu/opengl/gl_vertex_array.hh b/source/blender/gpu/opengl/gl_vertex_array.hh
new file mode 100644
index 00000000000..6da414d7e62
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_vertex_array.hh
@@ -0,0 +1,44 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "glew-mx.h"
+
+#include "GPU_batch.h"
+#include "GPU_shader_interface.h"
+
+namespace blender {
+namespace gpu {
+
+namespace GLVertArray {
+
+void update_bindings(const GLuint vao,
+ const GPUBatch *batch,
+ const GPUShaderInterface *interface,
+ const int base_instance);
+
+} // namespace GLVertArray
+
+} // namespace gpu
+} // namespace blender