first commit
This commit is contained in:
303
project/fm_viewer/fav/opengl/GeometryRenderer.cpp
Normal file
303
project/fm_viewer/fav/opengl/GeometryRenderer.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
/******************************************************************************
|
||||
QtAV: Multimedia framework based on Qt and FFmpeg
|
||||
Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
|
||||
|
||||
* This file is part of QtAV (from 2016)
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
******************************************************************************/
|
||||
#include "../GeometryRenderer.h"
|
||||
#if !(REMOVE_GEOMETRY_RENDERER)
|
||||
#include "OpenGLHelper.h"
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
#define QGLF(f) QOpenGLContext::currentContext()->functions()->f
|
||||
#else
|
||||
#define QGLF(f) QGLFunctions(NULL).f
|
||||
#endif
|
||||
namespace FAV {
|
||||
|
||||
GeometryRenderer::GeometryRenderer()
|
||||
: g(NULL)
|
||||
, features_(kVBO|kIBO|kVAO|kMapBuffer)
|
||||
, vbo_size(0)
|
||||
, ibo_size(0)
|
||||
, ibo(QOpenGLBuffer::IndexBuffer)
|
||||
, stride(0)
|
||||
{
|
||||
static bool disable_ibo = qgetenv("QTAV_NO_IBO").toInt() > 0;
|
||||
setFeature(kIBO, !disable_ibo);
|
||||
static bool disable_vbo = qgetenv("QTAV_NO_VBO").toInt() > 0;
|
||||
setFeature(kVBO, !disable_vbo);
|
||||
static bool disable_vao = qgetenv("QTAV_NO_VAO").toInt() > 0;
|
||||
setFeature(kVAO, !disable_vao);
|
||||
}
|
||||
|
||||
void GeometryRenderer::setFeature(int f, bool on)
|
||||
{
|
||||
if (on)
|
||||
features_ |= f;
|
||||
else
|
||||
features_ ^= f;
|
||||
}
|
||||
|
||||
void GeometryRenderer::setFeatures(int value)
|
||||
{
|
||||
features_ = value;
|
||||
}
|
||||
|
||||
int GeometryRenderer::features() const
|
||||
{
|
||||
return features_;
|
||||
}
|
||||
|
||||
int GeometryRenderer::actualFeatures() const
|
||||
{
|
||||
int f = 0;
|
||||
if (vbo.isCreated())
|
||||
f |= kVBO;
|
||||
if (ibo.isCreated())
|
||||
f |= kIBO;
|
||||
#if QT_VAO
|
||||
if (vao.isCreated())
|
||||
f |= kVAO;
|
||||
#endif
|
||||
return f;
|
||||
}
|
||||
|
||||
bool GeometryRenderer::testFeatures(int value) const
|
||||
{
|
||||
return !!(features() & value);
|
||||
}
|
||||
|
||||
void GeometryRenderer::updateGeometry(Geometry *geo)
|
||||
{
|
||||
g = geo;
|
||||
if (!g) {
|
||||
ibo.destroy();
|
||||
vbo.destroy();
|
||||
#if QT_VAO
|
||||
vao.destroy();
|
||||
#endif
|
||||
vbo_size = 0;
|
||||
ibo_size = 0;
|
||||
return;
|
||||
}
|
||||
static int support_map = -1;
|
||||
if (support_map < 0) {
|
||||
static const char* ext[] = { "GL_OES_mapbuffer", NULL};
|
||||
if (OpenGLHelper::isOpenGLES()) {
|
||||
support_map = QOpenGLContext::currentContext()->format().majorVersion() > 2 ||
|
||||
OpenGLHelper::hasExtension(ext);
|
||||
} else {
|
||||
support_map = 1;
|
||||
}
|
||||
}
|
||||
if (testFeatures(kIBO) && !ibo.isCreated()) {
|
||||
if (g->indexCount() > 0) {
|
||||
//qDebug("creating IBO...");
|
||||
if (!ibo.create())
|
||||
qDebug("IBO create error");
|
||||
}
|
||||
}
|
||||
if (ibo.isCreated()) {
|
||||
ibo.bind();
|
||||
const int bs = g->indexDataSize();
|
||||
if (bs == ibo_size) {
|
||||
void * p = NULL;
|
||||
if (support_map && testFeatures(kMapBuffer))
|
||||
p = ibo.map(QOpenGLBuffer::WriteOnly);
|
||||
if (p) {
|
||||
memcpy(p, g->constIndexData(), bs);
|
||||
ibo.unmap();
|
||||
} else {
|
||||
ibo.write(0, g->constIndexData(), bs);
|
||||
}
|
||||
} else {
|
||||
ibo.allocate(g->indexData(), bs); // TODO: allocate NULL and then map or BufferSubData?
|
||||
ibo_size = bs;
|
||||
}
|
||||
ibo.release();
|
||||
}
|
||||
if (testFeatures(kVBO) && !vbo.isCreated())
|
||||
{
|
||||
#if (DEBUG_QT_VAO)
|
||||
qDebug("creating VBO...");
|
||||
#endif
|
||||
if (!vbo.create())
|
||||
{
|
||||
qWarning("VBO create error");
|
||||
}
|
||||
}
|
||||
if (vbo.isCreated()) {
|
||||
vbo.bind();
|
||||
const int bs = g->vertexCount()*g->stride();
|
||||
/* Notes from https://www.opengl.org/sdk/docs/man/html/glBufferSubData.xhtml
|
||||
When replacing the entire data store, consider using glBufferSubData rather than completely recreating the data store with glBufferData. This avoids the cost of reallocating the data store.
|
||||
*/
|
||||
if (bs == vbo_size) { // vbo.size() error 0x501 on rpi, and query gl value can be slow
|
||||
void* p = NULL;
|
||||
if (support_map && testFeatures(kMapBuffer))
|
||||
p = vbo.map(QOpenGLBuffer::WriteOnly);
|
||||
if (p) {
|
||||
memcpy(p, g->constVertexData(), bs);
|
||||
vbo.unmap();
|
||||
} else {
|
||||
vbo.write(0, g->constVertexData(), bs);
|
||||
vbo_size = bs;
|
||||
}
|
||||
} else {
|
||||
vbo.allocate(g->vertexData(), bs);
|
||||
}
|
||||
vbo.release();
|
||||
}
|
||||
#if QT_VAO
|
||||
if (stride == g->stride() && attrib == g->attributes())
|
||||
return;
|
||||
stride = g->stride();
|
||||
attrib = g->attributes();
|
||||
|
||||
if (testFeatures(kVAO) && !vao.isCreated())
|
||||
{
|
||||
#if (DEBUG_QT_VAO)
|
||||
qDebug("creating VAO...");
|
||||
#endif
|
||||
if (!vao.create())
|
||||
{
|
||||
#if (DEBUG_QT_VAO)
|
||||
qDebug("VAO create error");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if (DEBUG_QT_VAO)
|
||||
qDebug("vao updated");
|
||||
#endif
|
||||
if (vao.isCreated()) // can not use vao binder because it will create a vao if necessary
|
||||
{
|
||||
vao.bind();
|
||||
}
|
||||
// can set data before vao bind
|
||||
if (!vao.isCreated())
|
||||
{
|
||||
return;
|
||||
}
|
||||
qDebug("geometry attributes changed, rebind vao...");
|
||||
// call once is enough if no feature and no geometry attribute is changed
|
||||
if (vbo.isCreated())
|
||||
{
|
||||
vbo.bind();
|
||||
for (int an = 0; an < g->attributes().size(); ++an) {
|
||||
// FIXME: assume bind order is 0,1,2...
|
||||
const Attribute& a = g->attributes().at(an);
|
||||
QGLF(glVertexAttribPointer(an, a.tupleSize(), a.type(), a.normalize(), g->stride(), reinterpret_cast<const void *>(qptrdiff(a.offset())))); //TODO: in setActiveShader
|
||||
QGLF(glEnableVertexAttribArray(an));
|
||||
}
|
||||
vbo.release(); // unbind after vao unbind? http://www.zwqxin.com/archives/opengl/vao-and-vbo-stuff.html
|
||||
} // TODO: bind pointers if vbo is disabled
|
||||
// bind ibo to vao thus no bind is required later
|
||||
if (ibo.isCreated())// if not bind here, glDrawElements(...,NULL) crashes and must use ibo data ptr, why?
|
||||
ibo.bind();
|
||||
vao.release();
|
||||
if (ibo.isCreated())
|
||||
ibo.release();
|
||||
#endif
|
||||
#if (DEBUG_QT_VAO)
|
||||
qDebug("geometry updated");
|
||||
#endif
|
||||
}
|
||||
|
||||
void GeometryRenderer::bindBuffers()
|
||||
{
|
||||
bool bind_vbo = vbo.isCreated();
|
||||
bool bind_ibo = ibo.isCreated();
|
||||
bool setv_skip = false;
|
||||
#if QT_VAO
|
||||
if (vao.isCreated()) {
|
||||
vao.bind(); // vbo, ibo is ok now
|
||||
setv_skip = bind_vbo;
|
||||
bind_vbo = false;
|
||||
bind_ibo = false;
|
||||
}
|
||||
#endif
|
||||
//qDebug("bind ibo: %d vbo: %d; set v: %d", bind_ibo, bind_vbo, !setv_skip);
|
||||
if (bind_ibo)
|
||||
ibo.bind();
|
||||
// no vbo: set vertex attributes
|
||||
// has vbo, no vao: bind vbo & set vertex attributes
|
||||
// has vbo, has vao: skip
|
||||
if (setv_skip)
|
||||
return;
|
||||
if (!g)
|
||||
return;
|
||||
const char* vdata = static_cast<const char*>(g->vertexData());
|
||||
if (bind_vbo) {
|
||||
vbo.bind();
|
||||
vdata = NULL;
|
||||
}
|
||||
for (int an = 0; an < g->attributes().size(); ++an) {
|
||||
const Attribute& a = g->attributes().at(an);
|
||||
QGLF(glVertexAttribPointer(an, a.tupleSize(), a.type(), a.normalize(), g->stride(), vdata + a.offset()));
|
||||
QGLF(glEnableVertexAttribArray(an)); //TODO: in setActiveShader
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryRenderer::unbindBuffers()
|
||||
{
|
||||
bool unbind_vbo = vbo.isCreated();
|
||||
bool unbind_ibo = ibo.isCreated();
|
||||
bool unsetv_skip = false;
|
||||
#if QT_VAO
|
||||
if (vao.isCreated()) {
|
||||
vao.release();
|
||||
unsetv_skip = unbind_vbo;
|
||||
unbind_vbo = false;
|
||||
unbind_ibo = false;
|
||||
}
|
||||
#endif //QT_VAO
|
||||
//qDebug("unbind ibo: %d vbo: %d; unset v: %d", unbind_ibo, unbind_vbo, !unsetv_skip);
|
||||
if (unbind_ibo)
|
||||
ibo.release();
|
||||
// release vbo. qpainter is affected if vbo is bound
|
||||
if (unbind_vbo)
|
||||
vbo.release();
|
||||
// no vbo: disable vertex attributes
|
||||
// has vbo, no vao: unbind vbo & set vertex attributes
|
||||
// has vbo, has vao: skip
|
||||
if (unsetv_skip)
|
||||
return;
|
||||
if (!g)
|
||||
return;
|
||||
for (int an = 0; an < g->attributes().size(); ++an) {
|
||||
QGLF(glDisableVertexAttribArray(an));
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryRenderer::render()
|
||||
{
|
||||
if (!g)
|
||||
return;
|
||||
bindBuffers();
|
||||
if (g->indexCount() > 0) {
|
||||
DYGL(glDrawElements(g->primitive(), g->indexCount(), g->indexType(), ibo.isCreated() ? NULL : g->indexData())); // null: data in vao or ibo. not null: data in memory
|
||||
#if (MODEL_360)
|
||||
//DYGL(glDrawElements(GL_LINE_LOOP, g->indexCount(), g->indexType(), ibo.isCreated() ? NULL : g->indexData())); // null: data in vao or ibo. not null: data in memory
|
||||
#endif
|
||||
} else {
|
||||
DYGL(glDrawArrays(g->primitive(), 0, g->vertexCount()));
|
||||
//DYGL(glDrawArrays(GL_LINE_LOOP, 0, g->vertexCount()));
|
||||
}
|
||||
unbindBuffers();
|
||||
}
|
||||
} //namespace FAV
|
||||
#endif // #if !(REMOVE_GEOMETRY_RENDERER)
|
||||
Reference in New Issue
Block a user