This is to improve debugging on older hardware that may not support 4.3 debug capabilities (like Macs). This avoids sprinkling glGetErrors manually. This might still be needed to find the root cause since not all functions are covered. This overrides the functions pointers that GLEW have already init. This is only enabled if using --debug-gpu option and the debug extension are not available. This also cleanup the usage of GLContext::debug_layer_support and use wrapper to set object labels.
366 lines
10 KiB
C++
366 lines
10 KiB
C++
/*
|
|
* 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
|
|
*
|
|
* GL implementation of GPUBatch.
|
|
* The only specificity of GL here is that it caches a list of
|
|
* Vertex Array Objects based on the bound shader interface.
|
|
*/
|
|
|
|
#include "BLI_assert.h"
|
|
|
|
#include "glew-mx.h"
|
|
|
|
#include "gpu_batch_private.hh"
|
|
#include "gpu_shader_private.hh"
|
|
|
|
#include "gl_backend.hh"
|
|
#include "gl_context.hh"
|
|
#include "gl_debug.hh"
|
|
#include "gl_index_buffer.hh"
|
|
#include "gl_primitive.hh"
|
|
#include "gl_vertex_array.hh"
|
|
|
|
#include "gl_batch.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;
|
|
vao_id_ = 0;
|
|
}
|
|
|
|
/* Create a new VAO object and store it in the cache. */
|
|
void GLVaoCache::insert(const GLShaderInterface *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) {
|
|
const_cast<GLShaderInterface *>(static_vaos.interfaces[i])->ref_remove(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 GLShaderInterface **)MEM_callocN(
|
|
dynamic_vaos.count * sizeof(GLShaderInterface *), "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 GLShaderInterface **)MEM_recallocN(
|
|
(void *)dynamic_vaos.interfaces, sizeof(GLShaderInterface *) * 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;
|
|
}
|
|
|
|
const_cast<GLShaderInterface *>(interface)->ref_add(this);
|
|
}
|
|
|
|
void GLVaoCache::remove(const GLShaderInterface *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 GLShaderInterface **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 = GLContext::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 GLShaderInterface **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) {
|
|
const_cast<GLShaderInterface *>(interfaces[i])->ref_remove(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 GLShaderInterface *interface)
|
|
{
|
|
const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
|
|
const GLShaderInterface **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 = GLContext::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. */
|
|
Shader *shader = GLContext::get()->shader;
|
|
GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface);
|
|
if (interface_ != 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;
|
|
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 vao_base_instance_;
|
|
}
|
|
|
|
GLuint GLVaoCache::vao_get(GPUBatch *batch)
|
|
{
|
|
this->context_check();
|
|
|
|
Shader *shader = GLContext::get()->shader;
|
|
GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface);
|
|
if (interface_ != interface) {
|
|
interface_ = 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)
|
|
{
|
|
}
|
|
|
|
GLBatch::~GLBatch()
|
|
{
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Drawing
|
|
* \{ */
|
|
|
|
void GLBatch::bind(int i_first)
|
|
{
|
|
GLContext::get()->state_manager->apply_state();
|
|
|
|
if (flag & GPU_BATCH_DIRTY) {
|
|
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(this->elem_()->restart_index());
|
|
}
|
|
#endif
|
|
|
|
/* Can be removed if GL 4.2 is required. */
|
|
if (!GLContext::base_instance_support && (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)
|
|
{
|
|
GL_CHECK_RESOURCES("Batch");
|
|
|
|
this->bind(i_first);
|
|
|
|
BLI_assert(v_count > 0 && i_count > 0);
|
|
|
|
GLenum gl_type = to_gl(prim_type);
|
|
|
|
if (elem) {
|
|
const GLIndexBuf *el = this->elem_();
|
|
GLenum index_type = to_gl(el->index_type_);
|
|
GLint base_index = el->index_base_;
|
|
void *v_first_ofs = el->offset_ptr(v_first);
|
|
|
|
if (GLContext::base_instance_support) {
|
|
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 (GLContext::base_instance_support) {
|
|
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
|
|
}
|
|
}
|
|
|
|
/** \} */
|