first commit

This commit is contained in:
2026-02-21 17:11:31 +09:00
commit 18b4338361
4001 changed files with 365464 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 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 "../ConvolutionShader.h"
#include "../VideoShader_p.h"
#if !(REMOVE_VIDEO_SHADER)
namespace FAV {
class ConvolutionShaderPrivate : public VideoShaderPrivate
{
public:
ConvolutionShaderPrivate() : VideoShaderPrivate()
, u_Kernel(-1)
, radius(1)
{
kernel.resize((2*radius+1)*(2*radius+1));
updateShaderCode();
}
void updateShaderCode() {
const int ks = (2*radius+1)*(2*radius+1);
header = QStringLiteral("uniform float u_Kernel[%1];").arg(ks).toUtf8();
QString s = QStringLiteral("vec4 sample2d(sampler2D tex, vec2 pos, int p) { vec4 c = vec4(0.0);");
const int kd = 2*radius+1;
for (int i = 0; i < ks; ++i) {
const int x = i % kd - radius;
const int y = i / kd - radius;
s += QStringLiteral("c += texture(tex, pos + u_texelSize[p]*vec2(%1.0,%2.0))*u_Kernel[%3];")
.arg(x).arg(y).arg(i);
}
s += "c.a = texture(tex, pos).a;"
"return c;}\n";
sample_func = s.toUtf8();
}
int u_Kernel;
int radius;
QVector<float> kernel;
QByteArray header, sample_func;
};
ConvolutionShader::ConvolutionShader()
: VideoShader(*new ConvolutionShaderPrivate())
{}
ConvolutionShader::ConvolutionShader(ConvolutionShaderPrivate &d)
: VideoShader(d)
{}
int ConvolutionShader::kernelRadius() const
{
return d_func().radius;
}
void ConvolutionShader::setKernelRadius(int value)
{
DPTR_D(ConvolutionShader);
if (d.radius == value)
return;
d.radius = value;
d.kernel.resize(kernelSize());
d.updateShaderCode();
rebuildLater();
}
int ConvolutionShader::kernelSize() const
{
return (2*kernelRadius() + 1)*(2*kernelRadius() + 1);
}
const char* ConvolutionShader::userShaderHeader(QOpenGLShader::ShaderType t) const
{
if (t == QOpenGLShader::Vertex)
return 0;
return kernelUniformHeader().constData();
}
const char* ConvolutionShader::userSample() const
{
return kernelSample().constData();
}
bool ConvolutionShader::setUserUniformValues()
{
setKernelUniformValue();
return true;
}
const QByteArray& ConvolutionShader::kernelUniformHeader() const
{
return d_func().header;
}
const QByteArray& ConvolutionShader::kernelSample() const
{
return d_func().sample_func;
}
void ConvolutionShader::setKernelUniformValue()
{
program()->setUniformValueArray("u_Kernel", kernel(), kernelSize(), 1);
}
} //namespace FAV
#endif #if !(REMOVE_VIDEO_SHADER)

View File

@@ -0,0 +1,546 @@
/******************************************************************************
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 "../Geometry.h"
#include <QtDebug>
#include <QtCore/qmath.h>
namespace FAV {
Attribute::Attribute(DataType type, int tupleSize, int offset, bool normalize)
: m_normalize(normalize)
, m_type(type)
, m_tupleSize(tupleSize)
, m_offset(offset)
{}
Attribute::Attribute(const QByteArray& name, DataType type, int tupleSize, int offset, bool normalize)
: m_normalize(normalize)
, m_type(type)
, m_tupleSize(tupleSize)
, m_offset(offset)
, m_name(name)
{}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const Attribute &a) {
dbg.nospace() << "attribute: " << a.name();
dbg.nospace() << ", offset " << a.offset();
dbg.nospace() << ", tupleSize " << a.tupleSize();
dbg.nospace() << ", dataType " << a.type();
dbg.nospace() << ", normalize " << a.normalize();
return dbg.space();
}
#endif
Geometry::Geometry(int vertexCount, int indexCount, DataType indexType)
: m_primitive(TriangleStrip)
, m_itype(indexType)
, m_vcount(vertexCount)
, m_icount(indexCount)
{}
int Geometry::indexDataSize() const
{
switch (indexType()) {
case TypeU16: return indexCount()*2;
case TypeU32: return indexCount()*4;
default: return indexCount();
}
}
void Geometry::setIndexValue(int index, int value)
{
switch (indexType()) {
case TypeU8: {
quint8* d = (quint8*)m_idata.constData();
*(d+index) = value;
}
break;
case TypeU16: {
quint16* d = (quint16*)m_idata.constData();
*(d+index) = value;
}
break;
case TypeU32: {
quint32* d = (quint32*)m_idata.constData();
*(d+index) = value;
}
break;
default:
break;
}
}
void Geometry::setIndexValue(int index, int v1, int v2, int v3)
{
// TODO: *(d + 3*index), *(d + 3*index + 1), *(d + 3*index + 2)
switch (indexType()) {
case TypeU8: {
quint8* d = (quint8*)m_idata.constData();
*(d+index++) = v1;
*(d+index++) = v2;
*(d+index++) = v2;
}
break;
case TypeU16: {
quint16* d = (quint16*)m_idata.constData();
*(d+index++) = v1;
*(d+index++) = v2;
*(d+index++) = v3;
}
break;
case TypeU32: {
quint32* d = (quint32*)m_idata.constData();
*(d+index++) = v1;
*(d+index++) = v2;
*(d+index++) = v3;
}
break;
default:
break;
}
}
void Geometry::dumpVertexData()
{
printf("vertex %p: ", m_vdata.constData());
const int n = stride()/sizeof(float);
for (int i = 0; i < m_vcount; ++i) {
const float* f = (const float*)(m_vdata.constData()+i*stride());
for (int j = 0; j < n; ++j) {
printf("%f, ", *(f+j));
}
printf(";");
}
printf("\n");fflush(0);
}
void Geometry::dumpIndexData()
{
switch (indexType()) {
case TypeU8: {
quint8* d = (quint8*)m_idata.constData();
for (int i = 0; i < m_icount; ++i) printf("%u, ", *(d+i));
}
break;
case TypeU16: {
quint16* d = (quint16*)m_idata.constData();
for (int i = 0; i < m_icount; ++i) printf("%u, ", *(d+i));
}
break;
case TypeU32: {
quint32* d = (quint32*)m_idata.constData();
for (int i = 0; i < m_icount; ++i) printf("%u, ", *(d+i));
}
break;
default:
break;
}
printf("\n");fflush(0);
}
void Geometry::allocate(int nbVertex, int nbIndex)
{
m_icount = nbIndex;
m_vcount = nbVertex;
m_vdata.resize(nbVertex*stride());
memset(m_vdata.data(), 0, m_vdata.size());
if (nbIndex <= 0) {
m_idata.clear(); // required?
return;
}
switch (indexType()) {
case TypeU8:
m_idata.resize(nbIndex*sizeof(quint8));
break;
case TypeU16:
m_idata.resize(nbIndex*sizeof(quint16));
break;
case TypeU32:
m_idata.resize(nbIndex*sizeof(quint32));
break;
default:
break;
}
memset((void*)m_idata.constData(), 0, m_idata.size());
}
bool Geometry::compare(const Geometry *other) const
{
// this == other: attributes and stride can be different
if (!other)
return false;
if (stride() != other->stride())
return false;
return attributes() == other->attributes();
}
TexturedGeometry::TexturedGeometry()
: Geometry()
, nb_tex(0)
, geo_rect(-1, 1, 2, -2) // (-1, -1, 2, 2) flip y
{
setVertexCount(4);
a = QVector<Attribute>()
<< Attribute(TypeF32, 2, 0)
<< Attribute(TypeF32, 2, 2*sizeof(float))
;
setTextureCount(1);
}
void TexturedGeometry::setTextureCount(int value)
{
if (value == nb_tex)
return;
texRect.resize(value);
nb_tex = value;
}
int TexturedGeometry::textureCount() const
{
return nb_tex;
}
void TexturedGeometry::setPoint(int index, const QPointF &p, const QPointF &tp, int texIndex)
{
setGeometryPoint(index, p);
setTexturePoint(index, tp, texIndex);
}
void TexturedGeometry::setGeometryPoint(int index, const QPointF &p)
{
float *v = (float*)(m_vdata.constData() + index*stride());
*v = p.x();
*(v+1) = p.y();
}
void TexturedGeometry::setTexturePoint(int index, const QPointF &tp, int texIndex)
{
float *v = (float*)(m_vdata.constData() + index*stride() + (texIndex+1)*2*sizeof(float));
*v = tp.x();
*(v+1) = tp.y();
}
void TexturedGeometry::setRect(const QRectF &r, const QRectF &tr, int texIndex)
{
setPoint(0, r.topLeft(), tr.topLeft(), texIndex);
setPoint(1, r.bottomLeft(), tr.bottomLeft(), texIndex);
switch (primitive()) {
case TriangleStrip:
setPoint(2, r.topRight(), tr.topRight(), texIndex);
setPoint(3, r.bottomRight(), tr.bottomRight(), texIndex);
break;
case TriangleFan:
setPoint(3, r.topRight(), tr.topRight(), texIndex);
setPoint(2, r.bottomRight(), tr.bottomRight(), texIndex);
break;
case Triangles:
break;
default:
break;
}
}
void TexturedGeometry::setGeometryRect(const QRectF &r)
{
geo_rect = r;
}
void TexturedGeometry::setTextureRect(const QRectF &tr, int texIndex)
{
if (texRect.size() <= texIndex)
texRect.resize(texIndex+1);
texRect[texIndex] = tr;
//qInfo() << "tr" << tr;
}
const QVector<Attribute>& TexturedGeometry::attributes() const
{
return a;
}
void TexturedGeometry::create()
{
allocate(vertexCount());
if (a.size()-1 < textureCount()) { // the first is position
for (int i = a.size()-1; i < textureCount(); ++i)
a << Attribute(TypeF32, 2, int((i+1)* 2*sizeof(float)));
} else {
a.resize(textureCount() + 1);
}
setGeometryPoint(0, geo_rect.topLeft());
setGeometryPoint(1, geo_rect.bottomLeft());
switch (primitive()) {
case TriangleStrip:
setGeometryPoint(2, geo_rect.topRight());
setGeometryPoint(3, geo_rect.bottomRight());
break;
case TriangleFan:
setGeometryPoint(3, geo_rect.topRight());
setGeometryPoint(2, geo_rect.bottomRight());
break;
case Triangles:
break;
default:
break;
}
for (int i = 0; i < texRect.size(); ++i) {
const QRectF tr = texRect[i];
setTexturePoint(0, tr.topLeft(), i);
setTexturePoint(1, tr.bottomLeft(), i);
switch (primitive()) {
case TriangleStrip:
setTexturePoint(2, tr.topRight(), i);
setTexturePoint(3, tr.bottomRight(), i);
break;
case TriangleFan:
setTexturePoint(3, tr.topRight(), i);
setTexturePoint(2, tr.bottomRight(), i);
break;
case Triangles:
break;
default:
break;
}
}
}
#if (TOP_DOWN_360)
#define HEMISPHERE_ONLY 1
#define HEMISPHERE_TEST 1
#define DRAW_DEBUG_GRAPH 0
Sphere::Sphere()
: TexturedGeometry()
, r(1)
{
setPrimitive(Triangles);
setResolution(64, 129);
a = QVector<Attribute>()
<< Attribute(TypeF32, 3, 0)
<< Attribute(TypeF32, 2, 3*sizeof(float));
}
#else // DURLA 360
Sphere::Sphere()
: TexturedGeometry()
, r(1)
{
setPrimitive(Triangles);
setResolution(64, 64);
//setResolution(128, 128);
a = QVector<Attribute>()
<< Attribute(TypeF32, 3, 0)
<< Attribute(TypeF32, 2, 3*sizeof(float));
}
#endif
void Sphere::setResolution(int w, int h)
{
ru = w;
rv = h;
#if (HEMISPHERE_ONLY)
setVertexCount((ru+1)*((rv/2)+1));
#else
setVertexCount((ru+1)*(rv+1));
#endif
}
void Sphere::setRadius(float value)
{
r = value;
}
float Sphere::radius() const
{
return r;
}
#if (TOP_DOWN_360)
void Sphere::create()
{
// RU = W, RV = H 가로세로 셀의 개수 * QUAD(2 triangle)
allocate(vertexCount(), ru*rv*3*2); // quads * 2 triangles,
if (a.size()-1 < nb_tex) { // the first is position
for (int i = a.size()-1; i < nb_tex; ++i)
a << Attribute(TypeF32, 2, 3*sizeof(float) + int(i* 2*sizeof(float)));
} else {
a.resize(nb_tex + 1);
}
// TODO: use geo_rect?
float *vd = (float*)m_vdata.constData();
const float dTheta = M_PI*2.0/float(ru); // 경도 변환 각도 (360 / x)
const float dPhi = M_PI/float(rv); // 위도 변환 각도 (180 / y)
#if(HEMISPHERE_ONLY)
// https://stackoverrun.com/ko/q/734419
#if (DRAW_DEBUG_GRAPH)
QString graph = "";
#endif
for (int lat = rv/2; lat < rv; ++lat) {
const float phi = M_PI_2 - float(lat+1)*dPhi; // 90 - 위도 * y
float cosPhi = qCos(phi);
const float sinPhi = qSin(phi);
// if(lat == rv-1) {
// cosPhi = 0;
// }
//const float v = 1.0f - float(lat)*dv; // flip y?
for (int lon = 0; lon <= ru; ++lon) {
const float theta = float(lon)*dTheta;
const float cosTheta = qCos(theta);
const float sinTheta = qSin(theta);
#if (DRAW_DEBUG_GRAPH)
int x = r*cosPhi*cosTheta * 1000.0; // X
int y = r*sinPhi * 1000.0; // Z
int z = r*cosPhi*sinTheta * 1000.0; // Y
graph += QString().sprintf("%02d,%02d,%03d,%03d,%03d\n",lat,lon,x,y,z);
#endif // DRAW_DEBUG_GRAPH
*vd++ = r*cosPhi*cosTheta*SPHERE_SCALE; // X = radius *
*vd++ = r*sinPhi*SPHERE_SCALE; // Y(Z) 1~-1
*vd++ = r*cosPhi*sinTheta*SPHERE_SCALE; // Z
// texture
for (int i = 0; i < nb_tex; ++i) {
// GRID
*vd++ = texRect[i].x()+texRect[i].width()/qreal(ru) * qreal(lon);
*vd++ = texRect[i].y()+texRect[i].height()/qreal(rv/2) * qreal(lat-rv/2);
}
}
}
#if (DRAW_DEBUG_GRAPH)
QFile file("C:\\home\\temp\\sphere.csv");
if(file.open(QIODevice::WriteOnly))
{
file.write("LAT,LON,X,Y,Z\n");
file.write(graph.toUtf8());
file.close();
}
qInfo() << graph;
#endif
// create index data
if (m_icount > 0) {
int idx = 0;
for (int lat = 0; lat < rv/2; ++lat) {
for (int lon = 0; lon < ru; ++lon) {
const int ring = lat*(ru+1) + lon;
const int ringNext = ring + ru+1;
setIndexValue(idx, ring, ringNext, ring+1);
setIndexValue(idx+3, ringNext, ringNext+1, ring+1);
idx += 6;
}
}
}
m_icount /= 2;
#else // HEMISPHERE_ONLY --> FULL SPHERE
for (int lat = 0; lat <= rv; ++lat) {
const float phi = M_PI_2 - float(lat)*dPhi;
const float cosPhi = qCos(phi);
const float sinPhi = qSin(phi);
//const float v = 1.0f - float(lat)*dv; // flip y?
for (int lon = 0; lon <= ru; ++lon) {
const float theta = float(lon)*dTheta;
const float cosTheta = qCos(theta);
const float sinTheta = qSin(theta);
//const float u = float(lon) * du;
*vd++ = r*cosPhi*cosTheta;//2.0*float(lon)/float(ru) -1.0;//
*vd++ = r*sinPhi;//2.0*float(lat)/float(rv)-1.0;//
*vd++ = r*cosPhi*sinTheta;
// texture
for (int i = 0; i < nb_tex; ++i) {
*vd++ = texRect[i].x()+texRect[i].width()/float(ru) * float(lon);
*vd++ = texRect[i].y()+texRect[i].height()/float(rv) * float(lat);
}
}
}
// create index data
if (m_icount > 0) {
int idx = 0;
for (int lat = 0; lat < rv; ++lat) {
for (int lon = 0; lon < ru; ++lon) {
const int ring = lat*(ru+1) + lon;
const int ringNext = ring + ru+1;
setIndexValue(idx, ring, ringNext, ring+1);
setIndexValue(idx+3, ringNext, ringNext+1, ring+1);
idx += 6;
}
}
}
#endif
}
#else // DUAL 360
void Sphere::create()
{
allocate(vertexCount(), ru*rv*3*2); // quads * 2 triangles,
if (a.size()-1 < nb_tex) { // the first is position
for (int i = a.size()-1; i < nb_tex; ++i)
a << Attribute(TypeF32, 2, 3*sizeof(float) + int(i* 2*sizeof(float)));
} else {
a.resize(nb_tex + 1);
}
// TODO: use geo_rect?
float *vd = (float*)m_vdata.constData();
const float dTheta = M_PI*2.0/float(ru);
const float dPhi = M_PI/float(rv);
//const float du = 1.0f/float(ru);
//const float dv = 1.0f/float(rv);
for (int lat = 0; lat <= rv; ++lat) {
const float phi = M_PI_2 - float(lat)*dPhi;
const float cosPhi = qCos(phi);
const float sinPhi = qSin(phi);
//const float v = 1.0f - float(lat)*dv; // flip y?
for (int lon = 0; lon <= ru; ++lon) {
const float theta = float(lon)*dTheta;
const float cosTheta = qCos(theta);
const float sinTheta = qSin(theta);
//const float u = float(lon) * du;
*vd++ = r*cosPhi*cosTheta;//2.0*float(lon)/float(ru) -1.0;//
*vd++ = r*sinPhi;//2.0*float(lat)/float(rv)-1.0;//
*vd++ = r*cosPhi*sinTheta;
for (int i = 0; i < nb_tex; ++i) {
*vd++ = texRect[i].x()+texRect[i].width()/float(ru) * float(lon);
*vd++ = texRect[i].y()+texRect[i].height()/float(rv) * float(lat);
}
}
}
// create index data
if (m_icount > 0) {
int idx = 0;
for (int lat = 0; lat < rv; ++lat) {
for (int lon = 0; lon < ru; ++lon) {
const int ring = lat*(ru+1) + lon;
const int ringNext = ring + ru+1;
setIndexValue(idx, ring, ringNext, ring+1);
setIndexValue(idx+3, ringNext, ringNext+1, ring+1);
idx += 6;
}
}
}
}
#endif // DURL 360
} //namespace FAV

View 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)

View File

@@ -0,0 +1,773 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV
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 "OpenGLHelper.h"
#include <string.h> //strstr
#include <QtCore/QCoreApplication>
#include <QtCore/QRegExp>
#include <QtGui/QMatrix4x4>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
#include <QtOpenGL/QGLFunctions>
#endif
#else
#include <QtGui/QGuiApplication>
#endif
#ifdef QT_OPENGL_DYNAMIC
#include <QtGui/QOpenGLFunctions_1_0>
#endif
#if QTAV_HAVE(EGL_CAPI) // && QTAV_HAVE(QT_EGL) //make sure no crash if no egl library
#define EGL_CAPI_NS
#include "capi/egl_api.h"
#endif //QTAV_HAVE(EGL_CAPI)
#include "../Logger.h"
#define BUG_GLES3_ANDROID 1 //FIXME: N7 android6 gles3 displays red images, only rgb32 is correct
namespace FAV {
namespace OpenGLHelper {
// glGetTexParameteriv is supported by es2 does not support GL_TEXTURE_INTERNAL_FORMAT.
/// 16bit (R16 e.g.) texture does not support >8bit a BE channel, fallback to 2 channel texture
int depth16BitTexture()
{
static int depth = qgetenv("QTAV_TEXTURE16_DEPTH").toInt() == 8 ? 8 : 16;//8 ? 8 : 16;
return depth;
}
bool useDeprecatedFormats()
{
static bool v = qgetenv("QTAV_GL_DEPRECATED").toInt() == 1;
return v;
}
QString removeComments(const QString &code)
{
QString c(code);
c.remove(QRegExp(QStringLiteral("(/\\*([^*]|(\\*+[^*/]))*\\*+/)|(//[^\r^\n]*)")));
return c;
}
/// current shader works fine for gles 2~3 only with commonShaderHeader(). It's mainly for desktop core profile
static QByteArray commonShaderHeader(QOpenGLShader::ShaderType type)
{
// TODO: check useDeprecatedFormats() or useDeprecated()?
QByteArray h;
if (isOpenGLES()) {
h += "precision mediump int;\n"
"precision mediump float;\n"
;
} else {
h += "#define highp\n"
"#define mediump\n"
"#define lowp\n"
;
}
if (type == QOpenGLShader::Fragment) {
// >=1.30: texture(sampler2DRect,...). 'texture' is defined in header
// we can't check GLSLVersion() here because it the actually version used can be defined by "#version"
h += "#if __VERSION__ < 130\n"
"#define texture texture2D\n"
"#else\n"
"#define texture2D texture\n"
"#endif // < 130\n"
;
}
return h;
}
QByteArray compatibleShaderHeader(QOpenGLShader::ShaderType type)
{
#if BUG_GLES3_ANDROID
if (isOpenGLES())
return commonShaderHeader(type);
#endif //BUG_GLES3_ANDROID
QByteArray h;
// #version directive must occur in a compilation unit before anything else, except for comments and white spaces. Default is 100 if not set
h.append("#version ").append(QByteArray::number(GLSLVersion()));
if (isOpenGLES() && QOpenGLContext::currentContext()->format().majorVersion() > 2)
h += " es";
h += "\n";
h += commonShaderHeader(type);
if (GLSLVersion() >= 130) { // gl(es) 3
if (type == QOpenGLShader::Vertex) {
h += "#define attribute in\n"
"#define varying out\n"
;
} else if (type == QOpenGLShader::Fragment) {
h += "#define varying in\n"
"#define gl_FragColor out_color\n" //can not starts with 'gl_'
"out vec4 gl_FragColor;\n"
;
}
}
return h;
}
int GLSLVersion()
{
static int v = -1;
if (v >= 0)
return v;
if (!QOpenGLContext::currentContext()) {
qWarning("%s: current context is null", __FUNCTION__);
return 0;
}
const char* vs = (const char*)DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION));
int major = 0, minor = 0;
// es: "OpenGL ES GLSL ES 1.00 (ANGLE 2.1.99...)" can use ""%*[ a-zA-Z] %d.%d" in sscanf, desktop: "2.1"
//QRegExp rx("(\\d+)\\.(\\d+)");
if (strncmp(vs, "OpenGL ES GLSL ES ", 18) == 0)
vs += 18;
if (sscanf(vs, "%d.%d", &major, &minor) == 2) {
v = major * 100 + minor;
} else {
qWarning("Failed to detect glsl version using GL_SHADING_LANGUAGE_VERSION!");
v = 110;
if (isOpenGLES())
v = QOpenGLContext::currentContext()->format().majorVersion() >= 3 ? 300 : 100;
}
return v;
}
bool isEGL()
{
static int is_egl = -1;
if (is_egl >= 0)
return !!is_egl;
#ifdef Q_OS_IOS
is_egl = 0;
return false;
#endif
if (isOpenGLES()) { //TODO: ios has no egl
is_egl = 1;
return true;
}
// angle has no QTAV_HAVE(QT_EGL). TODO: no assert in capi, or check egl loaded
#if QTAV_HAVE(EGL_CAPI) //&& QTAV_HAVE(QT_EGL) //make sure no crash if no egl library
if (!egl::api().loaded()) { //load twice, here and ns func call
is_egl = 0;
return false;
}
if (eglGetCurrentDisplay() != EGL_NO_DISPLAY) { //egl can be loaded but glx is used
is_egl = 1;
return true;
}
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
if (QGuiApplication::platformName().contains(QLatin1String("egl"))) {
is_egl = 1;
return true;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
if (QGuiApplication::platformName().contains(QLatin1String("xcb"))) {
is_egl = qgetenv("QT_XCB_GL_INTEGRATION") == "xcb_egl";
qDebug("xcb_egl=%d", is_egl);
return !!is_egl;
}
#endif //5.5.0
#endif
// we can use QOpenGLContext::currentContext()->nativeHandle().value<QEGLNativeContext>(). but gl context is required
if (QOpenGLContext::currentContext())
is_egl = 0;
return false;
}
bool isOpenGLES()
{
#ifdef QT_OPENGL_DYNAMIC
QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (ctx)
return ctx->isOpenGLES();
if (qstrcmp(qApp->metaObject()->className(), "QCoreApplication") == 0) // QGuiApplication is required by QOpenGLContext::openGLModuleType
return false;
// desktop openGLModuleType() can create es compatible context, so prefer QOpenGLContext::isOpenGLES().
// qApp->testAttribute(Qt::AA_UseOpenGLES) is what user requested, but not the result can be different. reproduce: dygl set AA_ShareOpenGLContexts|AA_UseOpenGLES, fallback to desktop (why?)
return QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL;
#endif //QT_OPENGL_DYNAMIC
#ifdef QT_OPENGL_ES_2
return true;
#endif //QT_OPENGL_ES_2
#if defined(QT_OPENGL_ES_2_ANGLE_STATIC) || defined(QT_OPENGL_ES_2_ANGLE)
return true;
#endif //QT_OPENGL_ES_2_ANGLE_STATIC
return false;
}
bool hasExtensionEGL(const char *exts[])
{
#if !QTAV_HAVE(EGL_CAPI)
Q_UNUSED(exts);
#endif
if (!isEGL())
return false;
#if QTAV_HAVE(EGL_CAPI)
static QList<QByteArray> supported;
if (supported.isEmpty()) {
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, NULL, NULL);
supported = QByteArray(eglQueryString(display, EGL_EXTENSIONS)).split(' ');
}
static bool print_exts = true;
if (print_exts) {
print_exts = false;
qDebug() << "EGL extensions: " << supported;
}
for (int i = 0; exts[i]; ++i) {
if (supported.contains(QByteArray(exts[i])))
return true;
}
#endif
return false;
}
bool hasExtension(const char *exts[])
{
const QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (!ctx) {
qWarning("no gl context for hasExtension");
return false;
}
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
const char *ext = (const char*)glGetString(GL_EXTENSIONS);
if (!ext)
return false;
#endif
for (int i = 0; exts[i]; ++i) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
if (ctx->hasExtension(exts[i]))
#else
if (strstr(ext, exts[i]))
#endif
return true;
}
return false;
}
bool isPBOSupported() {
// check pbo support
static bool support = false;
static bool pbo_checked = false;
if (pbo_checked)
return support;
const QOpenGLContext *ctx = QOpenGLContext::currentContext();
Q_ASSERT(ctx);
if (!ctx)
return false;
const char* exts[] = {
"GL_ARB_pixel_buffer_object",
"GL_EXT_pixel_buffer_object",
"GL_NV_pixel_buffer_object", //OpenGL ES
NULL
};
support = hasExtension(exts);
if (QOpenGLContext::currentContext()->format().majorVersion() > 2)
support = true;
pbo_checked = true;
return support;
}
typedef struct {
GLint internal_format;
GLenum format;
GLenum type;
} gl_param_t;
// es formats: ALPHA, RGB, RGBA, LUMINANCE, LUMINANCE_ALPHA
// es types: UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_5_5_5_1 (NO UNSIGNED_SHORT)
/*!
c: number of channels(components) in the plane
b: componet size
result is gl_param_compat[(c-1)+4*(b-1)]
*/
static const gl_param_t gl_param_compat[] = { // it's legacy
{ GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE},
{ GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE},
{ GL_RGB, GL_RGB, GL_UNSIGNED_BYTE},
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE},
{ GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, //2 x 8 fallback to ra
{0,0,0},
};
static const gl_param_t gl_param_3r16[] = {
{GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
{GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
{GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // 1 x 16
{GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // 2 x 16
{GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT}, // 3 x 16
{GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // 4 x 16
{0,0,0},
};
static const gl_param_t gl_param_desktop_fallback[] = {
{GL_RED, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8
{GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
{GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
{GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
{GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
{0,0,0},
};
static const gl_param_t gl_param_es3rg8[] = {
{GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
{GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 fallback to rg
{0,0,0},
};
//https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_rg.txt
// supported by ANGLE+D3D11
static const gl_param_t gl_param_es2rg[] = {
{GL_RED, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8 //es2: GL_EXT_texture_rg. R8, RG8 are for render buffer
{GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
{GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
{GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
{GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 fallback to rg
{0,0,0},
};
bool test_gl_param(const gl_param_t& gp, bool* has_16 = 0)
{
if (!QOpenGLContext::currentContext()) {
qWarning("%s: current context is null", __FUNCTION__);
return false;
}
GLuint tex;
DYGL(glGenTextures(1, &tex));
DYGL(glBindTexture(GL_TEXTURE_2D, tex));
while (DYGL(glGetError()) != GL_NO_ERROR) {}
DYGL(glTexImage2D(GL_TEXTURE_2D, 0, gp.internal_format, 64, 64, 0, gp.format, gp.type, NULL));
if (DYGL(glGetError()) != GL_NO_ERROR) {
DYGL(glDeleteTextures(1, &tex));
return false;
}
if (!gl().GetTexLevelParameteriv) {
qDebug("Do not support glGetTexLevelParameteriv. test_gl_param returns false");
DYGL(glDeleteTextures(1, &tex));
return false;
}
GLint param = 0;
//GL_PROXY_TEXTURE_2D and no glGenTextures?
#ifndef GL_TEXTURE_INTERNAL_FORMAT //only in desktop
#define GL_TEXTURE_INTERNAL_FORMAT 0x1003
#endif
gl().GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &param);
if (param != gp.internal_format) {
qDebug("Do not support texture internal format: %#x (result %#x)", gp.internal_format, param);
DYGL(glDeleteTextures(1, &tex));
return false;
}
if (!has_16) {
DYGL(glDeleteTextures(1, &tex));
return true;
}
*has_16 = false;
GLenum pname = 0;
#ifndef GL_TEXTURE_RED_SIZE
#define GL_TEXTURE_RED_SIZE 0x805C
#endif
#ifndef GL_TEXTURE_LUMINANCE_SIZE
#define GL_TEXTURE_LUMINANCE_SIZE 0x8060
#endif
switch (gp.format) {
case GL_RED: pname = GL_TEXTURE_RED_SIZE; break;
case GL_LUMINANCE: pname = GL_TEXTURE_LUMINANCE_SIZE; break;
}
param = 0;
if (pname)
gl().GetTexLevelParameteriv(GL_TEXTURE_2D, 0, pname, &param);
if (param) {
qDebug("16 bit texture depth: %d.\n", (int)param);
*has_16 = (int)param == 16;
}
DYGL(glDeleteTextures(1, &tex));
return true;
}
bool hasRG()
{
static int has_rg = -1;
if (has_rg >= 0)
return !!has_rg;
qDebug("check gl3 rg: %#X", gl_param_3r16[1].internal_format);
if (test_gl_param(gl_param_3r16[1])) {
has_rg = 1;
return true;
}
qDebug("check es3 rg: %#X", gl_param_es3rg8[1].internal_format);
if (test_gl_param(gl_param_es3rg8[1])) {
has_rg = 1;
return true;
}
qDebug("check GL_EXT_texture_rg");
static const char* ext[] = { "GL_EXT_texture_rg", 0}; //RED, RG, R8, RG8
if (hasExtension(ext)) {
qDebug("has extension GL_EXT_texture_rg");
has_rg = 1;
return true;
}
qDebug("check gl es>=3 rg");
if (QOpenGLContext::currentContext())
has_rg = isOpenGLES() && QOpenGLContext::currentContext()->format().majorVersion() > 2; // Mesa GLES3 does not support (from qt)
return has_rg;
}
static int has_16_tex = -1;
static const gl_param_t* get_gl_param()
{
if (!QOpenGLContext::currentContext()) {
qWarning("%s: current context is null", __FUNCTION__);
return gl_param_compat;
}
static gl_param_t* gp = 0;
if (gp)
return gp;
bool has_16 = false;
// [4] is always available
if (test_gl_param(gl_param_3r16[4], &has_16)) {
if (has_16 && depth16BitTexture() == 16)
gp = (gl_param_t*)gl_param_3r16;
else
gp = (gl_param_t*)gl_param_desktop_fallback;
has_16_tex = has_16;
if (!useDeprecatedFormats()) {
qDebug("using gl_param_%s", gp == gl_param_3r16? "3r16" : "desktop_fallback");
return gp;
}
} else if (test_gl_param(gl_param_es3rg8[4], &has_16)) { //3.0 will fail because no glGetTexLevelParameteriv
gp = (gl_param_t*)gl_param_es3rg8;
has_16_tex = has_16;
if (!useDeprecatedFormats()) {
qDebug("using gl_param_es3rg8");
return gp;
}
} else if (isOpenGLES()) {
if (QOpenGLContext::currentContext()->format().majorVersion() > 2)
gp = (gl_param_t*)gl_param_es3rg8; //for 3.0
else if (hasRG())
gp = (gl_param_t*)gl_param_es2rg;
has_16_tex = has_16;
if (gp && !useDeprecatedFormats()) {
qDebug("using gl_param_%s", gp == gl_param_es3rg8 ? "es3rg8" : "es2rg");
return gp;
}
}
qDebug("fallback to gl_param_compat");
gp = (gl_param_t*)gl_param_compat;
has_16_tex = false;
return gp;
}
bool has16BitTexture()
{
if (has_16_tex >= 0)
return !!has_16_tex;
if (!QOpenGLContext::currentContext()) {
qWarning("%s: current context is null", __FUNCTION__);
return false;
}
get_gl_param();
return !!has_16_tex;
}
typedef struct {
VideoFormat::PixelFormat pixfmt;
quint8 channels[4];
} reorder_t;
// use with gl_param_compat
static const reorder_t gl_channel_maps[] = {
{ VideoFormat::Format_ARGB32, {1, 2, 3, 0}},
{ VideoFormat::Format_ABGR32, {3, 2, 1, 0}}, // R->gl.?(a)->R
{ VideoFormat::Format_BGR24, {2, 1, 0, 3}},
{ VideoFormat::Format_BGR565, {2, 1, 0, 3}},
{ VideoFormat::Format_BGRA32, {2, 1, 0, 3}},
{ VideoFormat::Format_BGR32, {2, 1, 0, 3}},
{ VideoFormat::Format_BGR48LE,{2, 1, 0, 3}},
{ VideoFormat::Format_BGR48BE,{2, 1, 0, 3}},
{ VideoFormat::Format_BGR48, {2, 1, 0, 3}},
{ VideoFormat::Format_BGR555, {2, 1, 0, 3}},
// TODO: rgb444le/be etc
{ VideoFormat::Format_Invalid,{1, 2, 3}}
};
static QMatrix4x4 channelMap(const VideoFormat& fmt)
{
if (fmt.isPlanar()) //currently only for planar
return QMatrix4x4();
switch (fmt.pixelFormat()) {
case VideoFormat::Format_UYVY:
return QMatrix4x4(0.0f, 0.5f, 0.0f, 0.5f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
case VideoFormat::Format_YUYV:
return QMatrix4x4(0.5f, 0.0f, 0.5f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f);
case VideoFormat::Format_VYUY:
return QMatrix4x4(0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
case VideoFormat::Format_YVYU:
return QMatrix4x4(0.5f, 0.0f, 0.5f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
case VideoFormat::Format_VYU:
return QMatrix4x4(0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
default:
break;
}
const quint8 *channels = NULL;//{ 0, 1, 2, 3};
for (int i = 0; gl_channel_maps[i].pixfmt != VideoFormat::Format_Invalid; ++i) {
if (gl_channel_maps[i].pixfmt == fmt.pixelFormat()) {
channels = gl_channel_maps[i].channels;
break;
}
}
QMatrix4x4 m;
if (!channels)
return m;
m.fill(0);
for (int i = 0; i < 4; ++i) {
m(i, channels[i]) = 1;
}
qDebug() << m;
return m;
}
//template<typename T, size_t N> Q_CONSTEXPR size_t array_size(const T (&)[N]) { return N;} //does not support local type if no c++11
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
bool videoFormatToGL(const VideoFormat& fmt, GLint* internal_format, GLenum* data_format, GLenum* data_type, QMatrix4x4* mat)
{
typedef struct fmt_entry {
VideoFormat::PixelFormat pixfmt;
GLint internal_format;
GLenum format;
GLenum type;
} fmt_entry;
static const fmt_entry pixfmt_to_gles[] = {
{VideoFormat::Format_BGRA32, GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE }, //tested for angle
{VideoFormat::Format_RGB32, GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_Invalid, 0, 0, 0}
};
Q_UNUSED(pixfmt_to_gles);
static const fmt_entry pixfmt_to_desktop[] = {
{VideoFormat::Format_BGRA32, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE }, //bgra bgra works on win but not macOS
{VideoFormat::Format_RGB32, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE }, //FIXMEL endian check
//{VideoFormat::Format_BGRA32, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, //{2,1,0,3}
//{VideoFormat::Format_BGR24, GL_RGB, GL_BGR, GL_UNSIGNED_BYTE }, //{0,1,2,3}
#ifdef GL_UNSIGNED_SHORT_5_6_5_REV
{VideoFormat::Format_BGR565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, // es error, use channel map
#endif
#ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV
{VideoFormat::Format_RGB555, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
#endif
#ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV
{VideoFormat::Format_BGR555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
#endif
// TODO: BE formats not implemeted
{VideoFormat::Format_RGB48, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT }, //TODO: they are not work for ANGLE, and rgb16 works on desktop gl, so remove these lines to use rgb16?
{VideoFormat::Format_RGB48LE, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT },
{VideoFormat::Format_RGB48BE, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT },
{VideoFormat::Format_BGR48, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT }, //RGB16?
{VideoFormat::Format_BGR48LE, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT },
{VideoFormat::Format_BGR48BE, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT },
{VideoFormat::Format_RGBA64LE, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT },
{VideoFormat::Format_RGBA64BE, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT },
{VideoFormat::Format_BGRA64LE, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT },
{VideoFormat::Format_BGRA64BE, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT },
{VideoFormat::Format_Invalid, 0, 0, 0}
};
Q_UNUSED(pixfmt_to_desktop);
const fmt_entry *pixfmt_gl_entry = pixfmt_to_desktop;
if (OpenGLHelper::isOpenGLES())
pixfmt_gl_entry = pixfmt_to_gles;
// Very special formats, for which OpenGL happens to have direct support
static const fmt_entry pixfmt_gl_base[] = {
{VideoFormat::Format_RGBA32, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, // only tested for macOS, win, angle
{VideoFormat::Format_RGB24, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE },
{VideoFormat::Format_RGB565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
{VideoFormat::Format_BGR32, GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE }, //rgba(tested) or abgr, depending on endian
};
const VideoFormat::PixelFormat pixfmt = fmt.pixelFormat();
// can not use array size because pixfmt_gl_entry is set on runtime
for (const fmt_entry* e = pixfmt_gl_entry; e->pixfmt != VideoFormat::Format_Invalid; ++e) {
if (e->pixfmt == pixfmt) {
*internal_format = e->internal_format;
*data_format = e->format;
*data_type = e->type;
if (mat)
*mat = QMatrix4x4();
return true;
}
}
for (size_t i = 0; i < ARRAY_SIZE(pixfmt_gl_base); ++i) {
const fmt_entry& e = pixfmt_gl_base[i];
if (e.pixfmt == pixfmt) {
*internal_format = e.internal_format;
*data_format = e.format;
*data_type = e.type;
if (mat)
*mat = QMatrix4x4();
return true;
}
}
static const fmt_entry pixfmt_to_gl_swizzele[] = {
{VideoFormat::Format_VYU, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_UYVY, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_YUYV, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_VYUY, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_YVYU, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_BGR565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, //swizzle
{VideoFormat::Format_RGB555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, //not working
{VideoFormat::Format_BGR555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, //not working
};
for (size_t i = 0; i < ARRAY_SIZE(pixfmt_to_gl_swizzele); ++i) {
const fmt_entry& e = pixfmt_to_gl_swizzele[i];
if (e.pixfmt == pixfmt) {
*internal_format = e.internal_format;
*data_format = e.format;
*data_type = e.type;
if (mat)
*mat = channelMap(fmt);
return true;
}
}
GLint *i_f = internal_format;
GLenum *d_f = data_format;
GLenum *d_t = data_type;
gl_param_t* gp = (gl_param_t*)get_gl_param();
const int nb_planes = fmt.planeCount();
if (gp == gl_param_3r16 && (
//nb_planes == 2 || // nv12 UV plane is 16bit, but we use rg
(OpenGLHelper::depth16BitTexture() == 16 && OpenGLHelper::has16BitTexture() && fmt.isBigEndian() && fmt.bitsPerComponent() > 8) // 16bit texture does not support be channel now
)) {
gp = (gl_param_t*)gl_param_desktop_fallback;
qDebug("desktop_fallback for %s", nb_planes == 2 ? "bi-plane format" : "16bit big endian channel");
}
for (int p = 0; p < nb_planes; ++p) {
// for packed rgb(swizzle required) and planar formats
const int c = (fmt.channels(p)-1) + 4*((fmt.bitsPerComponent() + 7)/8 - 1);
if (gp[c].format == 0)
return false;
const gl_param_t& f = gp[c];
*(i_f++) = f.internal_format;
*(d_f++) = f.format;
*(d_t++) = f.type;
}
if (nb_planes > 2 && data_format[2] == GL_LUMINANCE && fmt.bytesPerPixel(1) == 1) { // QtAV uses the same shader for planar and semi-planar yuv format
internal_format[2] = data_format[2] = GL_ALPHA;
if (nb_planes == 4)
internal_format[3] = data_format[3] = data_format[2]; // vec4(,,,A)
}
if (mat)
*mat = channelMap(fmt);
return true;
}
// TODO: format + datatype? internal format == format?
//https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_format_BGRA8888.txt
// TODO: special format size, or componentsize(dataType)*components(format)
int bytesOfGLFormat(GLenum format, GLenum dataType) // TODO: rename bytesOfTexel
{
int component_size = 0;
switch (dataType) {
#ifdef GL_UNSIGNED_INT_8_8_8_8_REV
case GL_UNSIGNED_INT_8_8_8_8_REV:
return 4;
#endif
#ifdef GL_UNSIGNED_BYTE_3_3_2
case GL_UNSIGNED_BYTE_3_3_2:
return 1;
#endif //GL_UNSIGNED_BYTE_3_3_2
#ifdef GL_UNSIGNED_BYTE_2_3_3_REV
case GL_UNSIGNED_BYTE_2_3_3_REV:
return 1;
#endif
case GL_UNSIGNED_SHORT_5_5_5_1:
#ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV
case GL_UNSIGNED_SHORT_1_5_5_5_REV:
#endif //GL_UNSIGNED_SHORT_1_5_5_5_REV
#ifdef GL_UNSIGNED_SHORT_5_6_5_REV
case GL_UNSIGNED_SHORT_5_6_5_REV:
#endif //GL_UNSIGNED_SHORT_5_6_5_REV
case GL_UNSIGNED_SHORT_5_6_5: // gles
#ifdef GL_UNSIGNED_SHORT_4_4_4_4_REV
case GL_UNSIGNED_SHORT_4_4_4_4_REV:
#endif //GL_UNSIGNED_SHORT_4_4_4_4_REV
case GL_UNSIGNED_SHORT_4_4_4_4:
return 2;
case GL_UNSIGNED_BYTE:
component_size = 1;
break;
// mpv returns 2
#ifdef GL_UNSIGNED_SHORT_8_8_APPLE
case GL_UNSIGNED_SHORT_8_8_APPLE:
case GL_UNSIGNED_SHORT_8_8_REV_APPLE:
return 2;
#endif
case GL_UNSIGNED_SHORT:
component_size = 2;
break;
}
switch (format) {
case GL_RED:
case GL_LUMINANCE:
case GL_ALPHA:
return component_size;
case GL_RG:
case GL_LUMINANCE_ALPHA:
return 2*component_size;
#ifdef GL_YCBCR_422_APPLE
case GL_YCBCR_422_APPLE:
return 2;
#endif
#ifdef GL_RGB_422_APPLE
case GL_RGB_422_APPLE:
return 2;
#endif
#ifdef GL_BGR //ifndef GL_ES
case GL_BGR:
#endif
case GL_RGB:
return 3*component_size;
#ifdef GL_BGRA //ifndef GL_ES
case GL_BGRA:
#endif
case GL_RGBA:
return 4*component_size;
default:
qWarning("bytesOfGLFormat - Unknown format %u", format);
return 1;
}
}
} //namespace OpenGLHelper
} //namespace FAV

View File

@@ -0,0 +1,118 @@
/******************************************************************************
QtAV: Media play library based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV
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
******************************************************************************/
#ifndef QTAV_OPENGLHELPER_H
#define QTAV_OPENGLHELPER_H
#ifndef QT_NO_OPENGL
#include "../VideoFormat.h"
#include "gl_api.h"
// for dynamicgl. qglfunctions before qt5.3 does not have portable gl functions
#ifdef QT_OPENGL_DYNAMIC
#define DYGL(glFunc) QOpenGLContext::currentContext()->functions()->glFunc
#else
#define DYGL(glFunc) glFunc
#endif
#define EGL_ENSURE(x, ...) \
do { \
if (!(x)) { \
EGLint err = eglGetError(); \
qWarning("EGL error@%d<<%s. " #x ": %#x %s", __LINE__, __FILE__, err, eglQueryString(eglGetCurrentDisplay(), err)); \
return __VA_ARGS__; \
} \
} while(0)
#define EGL_WARN(x, ...) \
do { \
if (!(x)) { \
EGLint err = eglGetError(); \
qWarning("EGL error@%d<<%s. " #x ": %#x %s", __LINE__, __FILE__, err, eglQueryString(eglGetCurrentDisplay(), err)); \
} \
} while(0)
#define WGL_ENSURE(x, ...) \
do { \
if (!(x)) { \
qWarning() << "WGL error " << __FILE__ << "@" << __LINE__ << " " << #x << ": " << qt_error_string(GetLastError()); \
return __VA_ARGS__; \
} \
} while(0)
#define WGL_WARN(x, ...) \
do { \
if (!(x)) { \
qWarning() << "WGL error " << __FILE__ << "@" << __LINE__ << " " << #x << ": " << qt_error_string(GetLastError()); \
} \
} while(0)
QT_BEGIN_NAMESPACE
class QMatrix4x4;
QT_END_NAMESPACE
namespace FAV {
namespace OpenGLHelper {
QString removeComments(const QString& code);
QByteArray compatibleShaderHeader(QOpenGLShader::ShaderType type);
int GLSLVersion();
bool isEGL();
bool isOpenGLES();
/*!
* \brief hasExtensionEGL
* Test if any of the given extensions is supported
* \param exts Ends with NULL
* \return true if one of extension is supported
*/
bool hasExtensionEGL(const char* exts[]);
bool hasRG();
bool has16BitTexture();
// set by user (environment var "QTAV_TEXTURE16_DEPTH=8 or 16", default now is 8)
int depth16BitTexture();
// set by user (environment var "QTAV_GL_DEPRECATED=1")
bool useDeprecatedFormats();
/*!
* \brief hasExtension
* Test if any of the given extensions is supported. Current OpenGL context must be valid.
* \param exts Ends with NULL
* \return true if one of extension is supported
*/
bool hasExtension(const char* exts[]);
bool isPBOSupported();
/*!
* \brief videoFormatToGL
* \param fmt
* \param internal_format an array with size fmt.planeCount()
* \param data_format an array with size fmt.planeCount()
* \param data_type an array with size fmt.planeCount()
* \param mat channel reorder matrix used in glsl
* \return false if fmt is not supported
*/
bool videoFormatToGL(const VideoFormat& fmt, GLint* internal_format, GLenum* data_format, GLenum* data_type, QMatrix4x4* mat = NULL);
int bytesOfGLFormat(GLenum format, GLenum dataType = GL_UNSIGNED_BYTE);
} //namespace OpenGLHelper
} //namespace FAV
#else
namespace FAV {
namespace OpenGLHelper {
#define DYGL(f) f
inline bool isOpenGLES() {return false;}
} //namespace OpenGLHelper
} //namespace FAV
#endif //QT_NO_OPENGL
#endif // QTAV_OPENGLHELPER_H

View File

@@ -0,0 +1,300 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 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 "../OpenGLTypes.h"
#include "OpenGLHelper.h"
#include <QtCore/QRegExp>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include "../Logger.h"
namespace FAV {
struct uniform_type_name {
QByteArray name;
Uniform::Type type;
} uniform_type_names[] ={
{"sample2D", Uniform::Sampler},
{"bool", Uniform::Bool},
{"int", Uniform::Int},
{"uint", Uniform::Int},
{"float", Uniform::Float},
{"vec2", Uniform::Vec2},
{"vec3", Uniform::Vec3},
{"vec4", Uniform::Vec4},
{"mat2", Uniform::Mat2},
{"mat3", Uniform::Mat3},
{"mat4", Uniform::Mat4},
{"bvec2", Uniform::BVec2},
{"bvec3", Uniform::BVec3},
{"bvec4", Uniform::BVec4},
{"ivec2", Uniform::IVec2},
{"ivec3", Uniform::IVec3},
{"ivec4", Uniform::IVec4},
{"uvec2", Uniform::UVec2},
{"uvec3", Uniform::UVec3},
{"uvec4", Uniform::UVec4},
{"mat2x2", Uniform::Mat2},
{"mat3x3", Uniform::Mat3},
{"mat4x4", Uniform::Mat4},
{"dmat2", Uniform::DMat2},
{"dmat3", Uniform::DMat3},
{"dmat4", Uniform::DMat4},
};
static Uniform::Type UniformTypeFromName(const QByteArray& name)
{
for (const uniform_type_name* un = uniform_type_names; un < uniform_type_names + sizeof(uniform_type_names)/sizeof(uniform_type_names[0]); ++un) {
if (un->name == name)
return un->type;
}
return Uniform::Unknown;
}
static QByteArray UniformTypeToName(Uniform::Type ut)
{
for (const uniform_type_name* un = uniform_type_names; un < uniform_type_names + sizeof(uniform_type_names)/sizeof(uniform_type_names[0]); ++un) {
if (un->type == ut)
return un->name;
}
return "unknown";
}
Uniform::Uniform(Type tp, int count)
: dirty(true)
, location(-1)
, tuple_size(1)
, array_size(1)
, t(tp)
{
setType(tp, count);
}
Uniform& Uniform::setType(Type tp, int count)
{
t = tp;
array_size = count;
if (isVec()) {
tuple_size = (t >> (V+1)) & ((1<<3) - 1);
} else if (isMat()) {
tuple_size = (t >> (M+1)) & ((1<<3) - 1);
tuple_size *= tuple_size;
}
int element_size = sizeof(float);
if (isInt() || isUInt() || isBool()) {
element_size = sizeof(int);
}
data = QVector<int>(element_size/sizeof(int)*tupleSize()*arraySize());
return *this;
}
template<typename T> bool set_uniform_value(QVector<int>& dst, const T* v, int count)
{
Q_ASSERT(sizeof(T)*count <= sizeof(int)*dst.size() && "set_uniform_value: Bad type or array size");
// why not dst.constData()?
const QVector<int> old(dst);
memcpy((char*)dst.data(), (const char*)v, count*sizeof(T));
return old != dst;
}
template<> bool set_uniform_value<bool>(QVector<int>& dst, const bool* v, int count)
{
const QVector<int> old(dst);
for (int i = 0; i < count; ++i) {
dst[i] = *(v + i);
}
return old != dst;
}
void Uniform::set(const float &v, int count)
{
if (count <= 0)
count = tupleSize()*arraySize();
dirty = set_uniform_value(data, &v, count);
}
void Uniform::set(const int &v, int count)
{
if (count <= 0)
count = tupleSize()*arraySize();
dirty = set_uniform_value(data, &v, count);
}
void Uniform::set(const unsigned &v, int count)
{
if (count <= 0)
count = tupleSize()*arraySize();
dirty = set_uniform_value(data, &v, count);
}
void Uniform::set(const float *v, int count)
{
if (count <= 0)
count = tupleSize()*arraySize();
dirty = set_uniform_value(data, v, count);
}
void Uniform::set(const int *v, int count)
{
if (count <= 0)
count = tupleSize()*arraySize();
dirty = set_uniform_value(data, v, count);
}
void Uniform::set(const unsigned *v, int count)
{
if (count <= 0)
count = tupleSize()*arraySize();
dirty = set_uniform_value(data, v, count);
}
void Uniform::set(const QVariant &v)
{
if (tupleSize() > 1 || arraySize() > 1) {
if (isFloat()) { //TODO: what if QVector<qreal> but uniform is float?
set(v.value<QVector<float> >().data());
} else if (isInt() || isBool()) {
set(v.value<QVector<int> >().data());
} else if (isUInt()) {
set(v.value<QVector<unsigned> >().data());
} else if (type() == Uniform::Sampler) {
}
} else {
if (isFloat()) {
set(v.toFloat());
} else if (isInt() || isBool()) {
set(v.toInt());
} else if (isUInt()) {
set(v.toUInt());
} else if (type() == Uniform::Sampler) {
}
}
}
bool Uniform::setGL()
{
if (location < 0) {
return false;
}
switch (type()) {
case Uniform::Bool:
case Uniform::Int:
gl().Uniform1iv(location, arraySize(), address<int>());
break;
case Uniform::Float:
gl().Uniform1fv(location, arraySize(), address<float>());
break;
case Uniform::Vec2:
gl().Uniform2fv(location, arraySize(), address<float>());
break;
case Uniform::Vec3:
gl().Uniform3fv(location, arraySize(), address<float>());
break;
case Uniform::Vec4:
gl().Uniform4fv(location, arraySize(), address<float>());
break;
case Uniform::Mat2:
gl().UniformMatrix2fv(location, arraySize(), GL_FALSE, address<float>());
break;
case Uniform::Mat3:
gl().UniformMatrix3fv(location, arraySize(), GL_FALSE, address<float>());
break;
case Uniform::Mat4:
gl().UniformMatrix4fv(location, arraySize(), GL_FALSE, address<float>());
break;
case Uniform::IVec2:
gl().Uniform2iv(location, arraySize(), address<int>());
break;
case Uniform::IVec3:
gl().Uniform3iv(location, arraySize(), address<int>());
break;
case Uniform::IVec4:
gl().Uniform4iv(location, arraySize(), address<int>());
break;
default:
qDebug() << *this;
qWarning("Unsupported uniform type in Qt. You should use 'VideoShader::setUserUniformValues()' to call glUniformXXX or directly call glUniformXXX instead");
return false;
}
dirty = false;
return true;
}
#ifndef QT_NO_DEBUG_STREAM
Q_AV_EXPORT QDebug operator<<(QDebug dbg, const Uniform &u)
{
dbg.nospace() << "uniform " << UniformTypeToName(u.type()) << " " << u.name.constData();
if (u.arraySize() > 1) {
dbg.nospace() << "[" << u.arraySize() << "]";
}
dbg.nospace() << ", dirty: " << u.dirty;
dbg.nospace() << ", location: " << u.location << ", " << "tupleSize: " << u.tupleSize() << ", ";
if (u.isBool() || u.isInt()) {
dbg.nospace() << "value: " << u.value<int>();
} else if (u.isUInt()) {
dbg.nospace() << "value: " << u.value<unsigned>();
} else if (u.isDouble()) {
dbg.nospace() << "value: " << u.value<double>();
} else {
dbg.nospace() << "value: " << u.value<float>();
}
return dbg.space();
}
Q_AV_EXPORT QDebug operator<<(QDebug dbg, Uniform::Type ut);
#endif
QVector<Uniform> ParseUniforms(const QByteArray &text, GLuint programId = 0)
{
QVector<Uniform> uniforms;
const QString code = OpenGLHelper::removeComments(QString(text));
const QStringList lines = code.split(';');
// TODO: highp lowp etc.
const QString exp(QStringLiteral("\\s*uniform\\s+([\\w\\d]+)\\s+([\\w\\d]+)\\s*"));
const QString exp_array = exp + QStringLiteral("\\[(\\d+)\\]\\s*");
foreach (QString line, lines) {
line = line.trimmed();
if (!line.startsWith(QStringLiteral("uniform ")))
continue;
QRegExp rx(exp_array);
if (rx.indexIn(line) < 0) {
rx = QRegExp(exp);
if (rx.indexIn(line) < 0)
continue;
}
Uniform u;
const QStringList x = rx.capturedTexts();
//qDebug() << x;
u.name = x.at(2).toUtf8();
int array_size = 1;
if (x.size() > 3)
array_size = x[3].toInt();
const QByteArray t(x[1].toLatin1());
u.setType(UniformTypeFromName(t), array_size);
if (programId > 0)
u.location = gl().GetUniformLocation(programId, u.name.constData());
uniforms.append(u);
}
qDebug() << uniforms;
return uniforms;
}
} //namespace FAV

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
#include "ShaderManager.h"
#if !(REMOVE_SHADER_MANAGER)
#include "../VideoShader.h"
namespace FAV {
class ShaderManager::Private
{
public:
~Private() {
// TODO: thread safe required?
qDeleteAll(shader_cache.values());
shader_cache.clear();
}
QHash<qint32, VideoShader*> shader_cache;
};
ShaderManager::ShaderManager(QObject *parent) :
QObject(parent)
, d(new Private())
{
#if (MODEL_360)
dualMode = 0;
#endif
}
ShaderManager::~ShaderManager()
{
delete d;
d = 0;
}
VideoShader* ShaderManager::prepareMaterial(VideoMaterial *material, qint32 materialType)
{
#if (MODEL_360)
qint32 type = materialType != -1 ? materialType : material->type();
if(dualMode == 1)
{
type = 9999;
}
#else
const qint32 type = materialType != -1 ? materialType : material->type();
#endif
VideoShader *shader = d->shader_cache.value(type, 0);
if (shader)
{
return shader;
}
#if (DEBUG_SHADER_MANAGER)
qDebug() << QString("[ShaderManager] cache a new shader material type(%1): %2").arg(type).arg(VideoMaterial::typeName(type));
#endif
shader = material->createShader();
#if (MODEL_360)
shader->dualMode = dualMode;
#endif
shader->initialize();
d->shader_cache[type] = shader;
return shader;
}
} //namespace FAV
#endif //#if !(REMOVE_VIDEO_SHADER)

View File

@@ -0,0 +1,57 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV (from 2014)
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
******************************************************************************/
#ifndef QTAV_SHADERMANAGER_H
#define QTAV_SHADERMANAGER_H
#define REMOVE_SHADER_MANAGER 0
#if !(REMOVE_SHADER_MANAGER)
#include <QtCore/QObject>
#define DEBUG_SHADER_MANAGER 0
namespace FAV {
class VideoShader;
class VideoMaterial;
/*!
* \brief The ShaderManager class
* Cache VideoShader and shader programes for different video material type.
* TODO: ShaderManager does not change for a given vo, so we can expose VideoRenderer.shaderManager() to set custom shader. It's better than VideoRenderer.opengl() because OpenGLVideo exposes too many apis that may confuse user.
*/
class ShaderManager : public QObject
{
Q_OBJECT
public:
ShaderManager(QObject *parent = 0);
~ShaderManager();
VideoShader* prepareMaterial(VideoMaterial *material, qint32 materialType = -1);
// void setCacheSize(int value);
#if (MODEL_360)
int dualMode;
#endif
private:
class Private;
Private* d;
};
} //namespace FAV
#endif // QTAV_SHADERMANAGER_H
#endif // #if !(REMOVE_SHADER_MANAGER)

View File

@@ -0,0 +1,207 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 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 "SubImagesGeometry.h"
#if !(DO_NOT_USE_SUBTITLE)
#include "../Logger.h"
namespace FAV {
#define U8COLOR 0
static const int kMaxTexWidth = 4096; //FIXME: glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
// if texture1d, we can directly copy ASS_Image.bitmap without line by line copy, i.e. tiled, and upload only once
typedef struct {
float x, y; // depends on target rect
float tx, ty; // depends on texture size and rects layout
#if U8COLOR
union {
quint8 r, g, b, a; //to be normalized
quint32 rgba;
};
#else
float r, g, b, a;
#endif
} VertexData;
static VertexData* SetUnnormalizedVertexData(VertexData* v, int tx, int ty, int tw, int th, quint32 color, bool useIndecies)
{
#if U8COLOR
union {
quint8 r, g, b, a;
quint32 rgba;
};
r = color >> 24;
g = (color >> 16) & 0xff;
b = (color >> 8) & 0xff;
a = 255 - (color & 0xff);
#else
float r, g, b, a;
r = (float)(color >> 24)/255.0;
g = (float)((color >> 16) & 0xff)/255.0;
b = (float)((color >> 8) & 0xff)/255.0;
a = (float)(255 - (color & 0xff))/255.0;
#endif
// normalize later
v[0].tx = tx;
v[0].ty = ty;
v[1].tx = tx;
v[1].ty = ty + th;
v[2].tx = tx + tw;
v[2].ty = ty;
v[3].tx = tx + tw;
v[3].ty = ty + th;
#if U8COLOR
v[0].rgba = rgba;
v[1].rgba = rgba;
v[2].rgba = rgba;
v[3].rgba = rgba;
#else
#define SETC(x) x.r = r; x.g=g; x.b=b; x.a=a;
SETC(v[0]);
SETC(v[1]);
SETC(v[2]);
SETC(v[3]);
#endif
if (!useIndecies) {
v[4] = v[1];
v[5] = v[2];
return v + 6;
}
return v + 4;
}
static VertexData* SetVertexPositionAndNormalize(VertexData* v, float x, float y, float w, float h, float texW, float texH, bool useIndecies)
{
v[0].x = x;
v[0].y = y;
v[1].x = x;
v[1].y = y + h;
v[2].x = x + w;
v[2].y = y;
v[3].x = x + w;
v[3].y = y + h;
v[0].tx /= texW;
v[0].ty /= texH;
v[1].tx /= texW;
v[1].ty /= texH;
v[2].tx /= texW;
v[2].ty /= texH;
v[3].tx /= texW;
v[3].ty /= texH;
//qDebug("%f,%f<=%f,%f; %u,%u,%u,%u", v[3].x, v[3].y, v[3].tx, v[3].ty, v[3].r, v[3].g, v[3].b, v[3].a);
if (!useIndecies) {
v[4] = v[1];
v[5] = v[2];
return v + 6;
}
return v + 4;
}
SubImagesGeometry::SubImagesGeometry()
: Geometry()
, m_normalized(false)
, m_w(0)
, m_h(0)
{
setPrimitive(Geometry::Triangles);
m_attributes << Attribute(TypeF32, 2)
<< Attribute(TypeF32, 2, 2*sizeof(float))
#if U8COLOR
<< Attribute(TypeU8, 4, 4*sizeof(float), true);
#else
<< Attribute(TypeF32, 4, 4*sizeof(float));
#endif
}
bool SubImagesGeometry::setSubImages(const SubImageSet &images)
{
// TODO: operator ==
if (m_images == images)
return false;
m_images = images;
return true;
}
bool SubImagesGeometry::generateVertexData(const QRect &rect, bool useIndecies, int maxWidth)
{
if (maxWidth < 0)
maxWidth = kMaxTexWidth;
if (useIndecies)
allocate(4*m_images.images.size(), 6*m_images.images.size());
else
allocate(6*m_images.images.size());
qDebug("images: %d/%d, %dx%d", m_images.isValid(), m_images.images.size(), m_images.width(), m_images.height());
m_rects_upload.clear();
m_w = m_h = 0;
m_normalized = false;
if (!m_images.isValid())
return false;
int W = 0, H = 0;
int x = 0, h = 0;
VertexData* vd = (VertexData*)vertexData();
int index = 0;
foreach (const SubImage& i, m_images.images) {
if (x + i.stride > maxWidth && maxWidth > 0) {
W = qMax(W, x);
H += h;
x = 0;
h = 0;
}
// we use w instead of stride even if we must upload stride. when maping texture coordinates and view port coordinates, we can use the visual rects instead of stride, i.e. the geometry vertices are (x, y, w, h), not (x, y, stride, h)
m_rects_upload.append(QRect(x, H, i.stride, i.h));
vd = SetUnnormalizedVertexData(vd, x, H, i.w, i.h, i.color, useIndecies);
if (useIndecies) { // TODO: set only once because it never changes, use IBO
const int v0 = index*4/6;
setIndexValue(index, v0, v0+1, v0+2);
setIndexValue(index+3, v0+1, v0+2, v0+3);
index += 6;
}
x += i.w;
h = qMax(h, i.h);
}
W = qMax(W, x);
H += h;
m_w = W;
m_h = H;
//qDebug("sub texture %dx%d", m_w, m_h);
const float dx0 = rect.x();
const float dy0 = rect.y();
const float sx = float(rect.width())/float(m_images.width());
const float sy = float(rect.height())/float(m_images.height());
vd = (VertexData*)vertexData();
foreach (const SubImage& i, m_images.images) {
//qDebug() << rect;
//qDebug("i: %d,%d", i.x, i.y);
vd = SetVertexPositionAndNormalize(vd, dx0 + float(i.x)*sx, dy0 + float(i.y)*sy, i.w*sx, i.h*sy, m_w, m_h, useIndecies);
m_normalized = true;
}
return true;
}
int SubImagesGeometry::stride() const
{
return sizeof(VertexData);
}
} //namespace FAV
#endif // #if !(DO_NOT_USE_SUBTITLE)

View File

@@ -0,0 +1,58 @@
/******************************************************************************
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
******************************************************************************/
#ifndef QTAV_SUBIMAGESGEOMETRY_H
#define QTAV_SUBIMAGESGEOMETRY_H
#include "../Geometry.h"
#include "../SubImage.h"
#if !(DO_NOT_USE_SUBTITLE)
namespace FAV {
class SubImagesGeometry : public Geometry {
public:
SubImagesGeometry();
bool setSubImages(const SubImageSet& images);
/*!
* \brief generateVertexData
* \param rect rect render to. If it's viewport rect, and fit video aspect ratio, ass images created from video frame size needs a scale transform is required when rendering
* \param useIndecies
* \param maxWidth
* \return false if current SubImageSet is invalid
*/
bool generateVertexData(const QRect& rect, bool useIndecies = false, int maxWidth = -1);
// available after generateVertexData is called
int width() { return m_w;}
int height() { return m_h;}
int stride() const Q_DECL_OVERRIDE;
const QVector<Attribute>& attributes() const Q_DECL_OVERRIDE { return m_attributes;}
const SubImageSet& images() const { return m_images; }
const QVector<QRect>& uploadRects() const { return m_rects_upload;}
private:
using Geometry::allocate;
bool m_normalized;
int m_w, m_h;
QVector<Attribute> m_attributes;
SubImageSet m_images; // for texture upload parameters
QVector<QRect> m_rects_upload;
};
} //namespace FAV
#endif //QTAV_SUBIMAGESGEOMETRY_H
#endif // #if !(DO_NOT_USE_SUBTITLE)

View File

@@ -0,0 +1,141 @@
/******************************************************************************
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 "SubImagesRenderer.h"
#if !(REMOVE_SUBIMAGE_RENDERER)
#include "SubImagesGeometry.h"
#include "../GeometryRenderer.h"
namespace FAV {
#define GLSL(x) #x "\n"
static const char kVert[] = GLSL(
attribute vec4 a_Position;
attribute vec2 a_TexCoords;
attribute vec4 a_Color;
uniform mat4 u_Matrix;
varying vec2 v_TexCoords;
varying vec4 v_Color;
void main() {
gl_Position = u_Matrix * a_Position;
v_TexCoords = a_TexCoords;
v_Color = a_Color;
});
static const char kFrag[] = GLSL(
uniform sampler2D u_Texture;
varying vec2 v_TexCoords;
varying vec4 v_Color;
void main() {
gl_FragColor.rgb = v_Color.rgb;
gl_FragColor.a = v_Color.a*texture2D(u_Texture, v_TexCoords).r;
}
);
SubImagesRenderer::SubImagesRenderer()
: m_geometry(new SubImagesGeometry())
, m_renderer(new GeometryRenderer())
, m_tex(0)
{}
SubImagesRenderer::~SubImagesRenderer()
{
delete m_geometry;
delete m_renderer;
}
void SubImagesRenderer::render(const SubImageSet &ass, const QRect &target, const QMatrix4x4 &transform)
{
if (m_geometry->setSubImages(ass) || m_rect != target) {
m_rect = target;
if (!m_geometry->generateVertexData(m_rect, true))
return;
uploadTexture(m_geometry);
m_renderer->updateGeometry(m_geometry);
}
if (!m_program.isLinked()) {
m_program.removeAllShaders();
QByteArray vs(kVert);
vs.prepend(OpenGLHelper::compatibleShaderHeader(QOpenGLShader::Vertex));
m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, vs);
QByteArray fs(kFrag);
fs.prepend(OpenGLHelper::compatibleShaderHeader(QOpenGLShader::Fragment));
m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, fs);
// TODO: foreach geometry.attributes
m_program.bindAttributeLocation("a_Position", 0);
m_program.bindAttributeLocation("a_TexCoords", 1);
m_program.bindAttributeLocation("a_Color", 2);
if (!m_program.link())
qWarning() << m_program.log();
}
m_program.bind();
gl().ActiveTexture(GL_TEXTURE0);
DYGL(glBindTexture(GL_TEXTURE_2D, m_tex));
m_program.setUniformValue("u_Texture", 0);
m_program.setUniformValue("u_Matrix", transform*m_mat);
DYGL(glEnable(GL_BLEND));
if (m_geometry->images().format() == SubImageSet::ASS)
gl().BlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
else
gl().BlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
m_renderer->render();
DYGL(glDisable(GL_BLEND));
}
void SubImagesRenderer::setProjectionMatrixToRect(const QRectF &v)
{
m_mat.setToIdentity();
m_mat.ortho(v);
}
void SubImagesRenderer::uploadTexture(SubImagesGeometry *g)
{
if (!m_tex) {
DYGL(glGenTextures(1, &m_tex)); //TODO: delete
}
GLint internal_fmt;
GLenum data_type;
GLenum fmt;
if (g->images().format() == SubImageSet::ASS)
OpenGLHelper::videoFormatToGL(VideoFormat(VideoFormat::Format_Y8), &internal_fmt, &fmt, &data_type);
else //rgb32
OpenGLHelper::videoFormatToGL(VideoFormat(VideoFormat::Format_ARGB32), &internal_fmt, &fmt, &data_type);
DYGL(glBindTexture(GL_TEXTURE_2D, m_tex));
DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
DYGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
DYGL(glTexImage2D(GL_TEXTURE_2D, 0, internal_fmt, g->width(), g->height(), 0, fmt, data_type, NULL));
//glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (int i = 0; i < g->uploadRects().size(); ++i) {
const QRect& r = g->uploadRects().at(i);
const SubImage& sub = g->images().images.at(i);
DYGL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x(), r.y(), r.width(), r.height(), fmt, data_type, sub.data.constData()));
}
//glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
DYGL(glBindTexture(GL_TEXTURE_2D, 0));
}
} //namespace FAV
#endif //#if !(REMOVE_OEPNGL_SHADER)

View File

@@ -0,0 +1,77 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 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
******************************************************************************/
#ifndef QTAV_SUBIMAGESRENDERER_H
#define QTAV_SUBIMAGESRENDERER_H
#define REMOVE_SUBIMAGE_RENDERER 1
#if !(REMOVE_SUBIMAGE_RENDERER)
#include <QtGui/QMatrix4x4>
#include <../SubImage.h>
#include <../OpenGLTypes.h>
#include "OpenGLHelper.h"
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLShader>
#else
#include <QtOpenGL/QGLShaderProgram>
#include <QtOpenGL/QGLShader>
#undef QOpenGLShaderProgram
#undef QOpenGLShader
#define QOpenGLShaderProgram QGLShaderProgram
#define QOpenGLShader QGLShader
#endif
namespace FAV {
class SubImagesGeometry;
class GeometryRenderer;
class SubImagesRenderer
{
public:
SubImagesRenderer();
~SubImagesRenderer();
/*!
* \brief render
* \param ass
* \param target
* \param transform additional transform, e.g. aspect ratio
*/
void render(const SubImageSet& ass, const QRect& target, const QMatrix4x4& transform = QMatrix4x4());
/*!
* \brief setProjectionMatrixToRect
* the rect will be viewport
*/
void setProjectionMatrixToRect(const QRectF& v);
private:
void uploadTexture(SubImagesGeometry* g);
SubImagesGeometry *m_geometry;
GeometryRenderer *m_renderer;
QMatrix4x4 m_mat;
QRect m_rect;
GLuint m_tex;
QOpenGLShaderProgram m_program;
};
} //namespace FAV
#endif // QTAV_SUBIMAGESRENDERER_H
#endif // #if !(REMOVE_SUBIMAGE_RENDERER)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,204 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 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 "../VideoShaderObject.h"
#if !(REMOVE_VIDEO_SHADER)
#include "../VideoShader_p.h"
#include <QtCore/QEvent>
#include <QtCore/QMetaProperty>
#include <QtCore/QSignalMapper>
namespace FAV {
class VideoShaderObjectPrivate : public VideoShaderPrivate
{
public:
~VideoShaderObjectPrivate() {
qDeleteAll(sigMap[VertexShader]);
qDeleteAll(sigMap[FragmentShader]);
sigMap[VertexShader].clear();
sigMap[FragmentShader].clear();
}
QVector<QSignalMapper*> sigMap[ShaderTypeCount];
};
VideoShaderObject::VideoShaderObject(QObject *parent)
: QObject(parent)
, VideoShader(*new VideoShaderObjectPrivate())
{}
VideoShaderObject::VideoShaderObject(VideoShaderObjectPrivate &d, QObject *parent)
: QObject(parent)
, VideoShader(d)
{}
bool VideoShaderObject::event(QEvent *event)
{
DPTR_D(VideoShaderObject);
if (event->type() != QEvent::DynamicPropertyChange)
return QObject::event(event);
QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent*>(event);
for (int shaderType = VertexShader; shaderType < ShaderTypeCount; ++shaderType) {
QVector<Uniform> &uniforms = d.user_uniforms[shaderType];
for (int i = 0; i < uniforms.size(); ++i) {
if (uniforms.at(i).name == e->propertyName()) {
propertyChanged(i|(shaderType<<16));
}
}
}
return QObject::event(event);
}
void VideoShaderObject::propertyChanged(int id)
{
DPTR_D(VideoShaderObject);
const int st = id>>16;
const int idx = id&0xffff;
Uniform &u = d.user_uniforms[st][idx];
const QVariant v = property(u.name.constData());
u.set(v);
//if (u.dirty) update();
}
void VideoShaderObject::programReady()
{
DPTR_D(VideoShaderObject);
// find property name. if has property, bind to property
for (int st = VertexShader; st < ShaderTypeCount; ++st) {
qDeleteAll(d.sigMap[st]);
d.sigMap[st].clear();
const QVector<Uniform> &uniforms = d.user_uniforms[st];
for (int i = 0; i < uniforms.size(); ++i) {
const Uniform& u = uniforms[i];
const int idx = metaObject()->indexOfProperty(u.name.constData());
if (idx < 0) {
qDebug("VideoShaderObject has no meta property '%s'. Setting initial value from dynamic property", u.name.constData());
const_cast<Uniform&>(u).set(property(u.name.constData()));
continue;
}
QMetaProperty mp = metaObject()->property(idx);
if (!mp.hasNotifySignal()) {
qWarning("VideoShaderObject property '%s' has no signal", mp.name());
continue;
}
QMetaMethod mm = mp.notifySignal();
QSignalMapper *mapper = new QSignalMapper();
mapper->setMapping(this, i|(st<<16));
#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
connect(this, mm, mapper, mapper->metaObject()->method(mapper->metaObject()->indexOfSlot("map()")));
#else
#endif
connect(mapper, SIGNAL(mapped(int)), this, SLOT(propertyChanged(int)));
d.sigMap[st].append(mapper);
qDebug() << "set uniform property: " << u.name << property(u.name.constData());
propertyChanged(i|(st<<16)); // set the initial value
}
}
//ready();
}
class DynamicShaderObjectPrivate : public VideoShaderObjectPrivate {
public:
QString header;
QString sampleFunc;
QString pp;
};
DynamicShaderObject::DynamicShaderObject(QObject *parent)
: VideoShaderObject(*new DynamicShaderObjectPrivate(), parent)
{}
DynamicShaderObject::DynamicShaderObject(DynamicShaderObjectPrivate &d, QObject *parent)
: VideoShaderObject(d, parent)
{}
QString DynamicShaderObject::header() const
{
return d_func().header;
}
void DynamicShaderObject::setHeader(const QString &text)
{
DPTR_D(DynamicShaderObject);
if (d.header == text)
return;
d.header = text;
Q_EMIT headerChanged();
rebuildLater();
}
QString DynamicShaderObject::sample() const
{
return d_func().sampleFunc;
}
void DynamicShaderObject::setSample(const QString &text)
{
DPTR_D(DynamicShaderObject);
if (d.sampleFunc == text)
return;
d.sampleFunc = text;
Q_EMIT sampleChanged();
rebuildLater();
}
QString DynamicShaderObject::postProcess() const
{
return d_func().pp;
}
void DynamicShaderObject::setPostProcess(const QString &text)
{
DPTR_D(DynamicShaderObject);
if (d.pp == text)
return;
d.pp = text;
Q_EMIT postProcessChanged();
rebuildLater();
}
const char* DynamicShaderObject::userShaderHeader(QOpenGLShader::ShaderType st) const
{
if (st == QOpenGLShader::Vertex)
return 0;
if (d_func().header.isEmpty())
return 0;
return d_func().header.toUtf8().constData();
}
const char* DynamicShaderObject::userSample() const
{
if (d_func().sampleFunc.isEmpty())
return 0;
return d_func().sampleFunc.toUtf8().constData();
}
const char* DynamicShaderObject::userPostProcess() const
{
if (d_func().pp.isEmpty())
return 0;
return d_func().pp.toUtf8().constData();
}
} //namespace FAV
#endif // #if !(REMOVE_VIDEO_SHADER)

View File

@@ -0,0 +1,179 @@
/******************************************************************************
QtAV: Media play library based on Qt and FFmpeg
Copyright (C) 2012-2016 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 "gl_api.h"
#include "OpenGLHelper.h"
namespace FAV {
typedef void *(*GetProcAddress_t)(const char *);
static GetProcAddress_t sGetProcAddress;
void* GetProcAddress_Qt(const char *name)
{
if (!QOpenGLContext::currentContext())
return 0;
void* p = (void*)QOpenGLContext::currentContext()->getProcAddress(QByteArray((const char*)name));
if (!p) {
#if defined(Q_OS_WIN) && defined(QT_OPENGL_DYNAMIC)
HMODULE handle = (HMODULE)QOpenGLContext::openGLModuleHandle();
if (handle)
p = (void*)GetProcAddress(handle, name);
#endif
}
//fallback to QOpenGLFunctions_1_0?
return p;
}
static void* GetProcAddressWithExt(GetProcAddress_t get, const char *name)
{
void* fp = get(name);
if (fp)
return fp;
static const char *ext[] = {
"ARB", "OES", "EXT", "ANGLE", "NV" //TODO: MESA, INTEL?
#ifdef __APPLE__
, "APPLE"
#endif
, NULL
};
char f[512];
memcpy(f, name, strlen(name));
char* const p = f + strlen(name);
for (int i = 0; ext[i]; ++i) {
memcpy(p, ext[i], sizeof(ext[i]) + 1); //copy trail '\0'
fp = get(f);
if (fp) {
printf("extension resolved: %s", f);
return fp;
}
}
return NULL;
}
static void* GetProcAddressDefault(const char *name)
{
return GetProcAddressWithExt(GetProcAddress_Qt, name);
}
#ifdef QT_OPENGL_DYNAMIC
#define GETPROCADDRESS_RESOLVE
// GL_RESOLVE_ES_X_X will link to lib or use getProcAddress
#define GL_RESOLVE_ES_2_0(name) GL_RESOLVE(name)
#define GL_RESOLVE_ES_3_0(name) GL_RESOLVE(name)
#define GL_RESOLVE_ES_3_1(name) GL_RESOLVE(name)
#else
#ifdef GL_ES_VERSION_2_0
#define GL_RESOLVE_ES_2_0(name) GL_RESOLVE(name)
#define GL_RESOLVE_ES_3_0(name) GL_RESOLVE_EXT(name)
#define GL_RESOLVE_ES_3_1(name) GL_RESOLVE_EXT(name)
#elif GL_ES_VERSION_3_0
#define GL_RESOLVE_ES_2_0(name) GL_RESOLVE(name)
#define GL_RESOLVE_ES_3_0(name) GL_RESOLVE(name)
#define GL_RESOLVE_ES_3_1(name) GL_RESOLVE_NONE(name) //gl3ext is empty
#elif GL_ES_VERSION_3_1
#define GL_RESOLVE_ES_2_0(name) GL_RESOLVE(name)
#define GL_RESOLVE_ES_3_0(name) GL_RESOLVE(name)
#define GL_RESOLVE_ES_3_1(name) GL_RESOLVE(name)
#else
#define GETPROCADDRESS_RESOLVE
// GL_RESOLVE_ES_X_X will link to lib or use getProcAddress
#define GL_RESOLVE_ES_2_0(name) GL_RESOLVE(name)
#define GL_RESOLVE_ES_3_0(name) GL_RESOLVE(name)
#define GL_RESOLVE_ES_3_1(name) GL_RESOLVE(name)
#endif
#endif //QT_OPENGL_DYNAMIC
#define GL_RESOLVE_NONE(name) do { name = NULL;}while(0)
#define GL_RESOLVE_EXT(name) do {\
void** fp = (void**)(&name); \
*fp = GetProcAddressDefault("gl" # name); \
} while(0)
#ifdef GETPROCADDRESS_RESOLVE
#define GL_RESOLVE(name) GL_RESOLVE_EXT(name)
#else
#define GL_RESOLVE(name) do {\
name = ::gl##name; \
} while(0)
#endif
#define WGL_RESOLVE(name) do {\
void** fp = (void**)(&name); \
*fp = sGetProcAddress("wgl" # name); \
} while(0)
void api::resolve()
{
//memset(g, 0, sizeof(g));
sGetProcAddress = GetProcAddressDefault;
GL_RESOLVE(GetString);
GL_RESOLVE(GetError);
GL_RESOLVE(ActiveTexture);
GL_RESOLVE(BindFramebuffer);
GL_RESOLVE(GetUniformLocation);
GL_RESOLVE(Uniform1f);
GL_RESOLVE(Uniform2f);
GL_RESOLVE(Uniform3f);
GL_RESOLVE(Uniform4f);
GL_RESOLVE(Uniform1fv);
GL_RESOLVE(Uniform2fv);
GL_RESOLVE(Uniform3fv);
GL_RESOLVE(Uniform4fv);
GL_RESOLVE(Uniform1iv);
GL_RESOLVE(Uniform2iv);
GL_RESOLVE(Uniform3iv);
GL_RESOLVE(Uniform4iv);
GL_RESOLVE(UniformMatrix2fv);
GL_RESOLVE(UniformMatrix3fv);
GL_RESOLVE(UniformMatrix4fv);
GL_RESOLVE(BlendFuncSeparate);
GL_RESOLVE_ES_3_1(GetTexLevelParameteriv);
#ifdef Q_OS_WIN32
if (!OpenGLHelper::isOpenGLES()) {
static const char* ext[] = {
"WGL_NV_DX_interop2",
"WGL_NV_DX_interop",
NULL,
};
if (OpenGLHelper::hasExtension(ext)) { // TODO: use wgl getprocaddress function (for qt4)
qDebug("resolving WGL_NV_DX_interop...");
WGL_RESOLVE(DXSetResourceShareHandleNV);
WGL_RESOLVE(DXOpenDeviceNV);
WGL_RESOLVE(DXCloseDeviceNV);
WGL_RESOLVE(DXRegisterObjectNV);
WGL_RESOLVE(DXUnregisterObjectNV);
WGL_RESOLVE(DXObjectAccessNV);
WGL_RESOLVE(DXLockObjectsNV);
WGL_RESOLVE(DXUnlockObjectsNV);
}
}
#endif //Q_OS_WIN32
}
api& gl() {
static api g;
if (!sGetProcAddress) {
g.resolve();
}
return g;
}
} //namespace FAV

View File

@@ -0,0 +1,170 @@
/******************************************************************************
QtAV: Media play library based on Qt and FFmpeg
Copyright (C) 2012-2016 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
******************************************************************************/
#ifndef QTAV_GL_API_H
#define QTAV_GL_API_H
#ifndef QT_NO_OPENGL
#include <qglobal.h>
# if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QtGui/QOpenGLBuffer>
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QOpenGLShaderProgram>
# elif defined(QT_OPENGL_LIB)
# if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
#include <QtOpenGL/QGLFunctions>
# endif //4.8
#include <QtOpenGL/QGLBuffer>
#include <QtOpenGL/QGLContext>
#include <QtOpenGL/QGLShaderProgram>
#define QOpenGLShaderProgram QGLShaderProgram
typedef QGLBuffer QOpenGLBuffer;
#define QOpenGLContext QGLContext
#define QOpenGLShaderProgram QGLShaderProgram
#define QOpenGLShader QGLShader
#define QOpenGLFunctions QGLFunctions
#define initializeOpenGLFunctions() initializeGLFunctions()
#include <qgl.h>
# else //used by vaapi even qtopengl module is disabled
# if defined(QT_OPENGL_ES_2)
# if defined(Q_OS_MAC) // iOS
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
# else // "uncontrolled" ES2 platforms
#include <GLES2/gl2.h>
# endif // Q_OS_MAC
# else // non-ES2 platforms
# if defined(Q_OS_MAC)
#include <OpenGL/gl.h>
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
#include <OpenGL/gl3.h>
# endif
#include <OpenGL/glext.h>
# else
#include <GL/gl.h>
# endif // Q_OS_MAC
# endif // QT_OPENGL_ES_2
# endif
#ifndef GL_APIENTRY
#ifdef Q_OS_WIN
#define GL_APIENTRY __stdcall
#else
#define GL_APIENTRY
#endif
#endif
#ifndef GL_TEXTURE_RECTANGLE
#define GL_TEXTURE_RECTANGLE 0x84F5
#endif
//GL_BGRA is available in OpenGL >= 1.2
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
#ifndef GL_BGR
#define GL_BGR 0x80E0
#endif
#ifndef GL_RED
#define GL_RED 0x1903
#endif
#ifndef GL_RG
#define GL_RG 0x8227
#endif
#ifndef GL_R8
#define GL_R8 0x8229
#endif
#ifndef GL_R16
#define GL_R16 0x822A
#endif
#ifndef GL_RG8
#define GL_RG8 0x822B
#endif
#ifndef GL_RG16
#define GL_RG16 0x822C
#endif
#ifndef GL_RGB8
#define GL_RGB8 0x8051
#endif
#ifndef GL_RGB16
#define GL_RGB16 0x8054
#endif
#ifndef GL_RGBA8
#define GL_RGBA8 0x8058
#endif
#ifndef GL_RGBA16
#define GL_RGBA16 0x805B
#endif
namespace FAV {
typedef char GLchar; // for qt4 mingw
struct api;
api& gl();
struct api {
void resolve();
// TODO: static, so gl::GetString
const GLubyte *(GL_APIENTRY *GetString)(GLenum);
GLenum (GL_APIENTRY *GetError)(void);
void (GL_APIENTRY *ActiveTexture)(GLenum);
void (GL_APIENTRY *BindFramebuffer)(GLenum target, GLuint framebuffer);
GLint (GL_APIENTRY *GetUniformLocation)(GLuint, const GLchar *);
void (GL_APIENTRY *Uniform1f)(GLint, GLfloat);
void (GL_APIENTRY *Uniform2f)(GLint, GLfloat, GLfloat);
void (GL_APIENTRY *Uniform3f)(GLint, GLfloat, GLfloat, GLfloat);
void (GL_APIENTRY *Uniform4f)(GLint, GLfloat, GLfloat, GLfloat, GLfloat);
void (GL_APIENTRY *Uniform1fv)(GLint location, GLsizei count, const GLfloat *value);
void (GL_APIENTRY *Uniform2fv)(GLint location, GLsizei count, const GLfloat *value);
void (GL_APIENTRY *Uniform3fv)(GLint location, GLsizei count, const GLfloat *value);
void (GL_APIENTRY *Uniform4fv)(GLint location, GLsizei count, const GLfloat *value);
void (GL_APIENTRY *Uniform1iv)(GLint location, GLsizei count, const GLint *value);
void (GL_APIENTRY *Uniform2iv)(GLint location, GLsizei count, const GLint *value);
void (GL_APIENTRY *Uniform3iv)(GLint location, GLsizei count, const GLint *value);
void (GL_APIENTRY *Uniform4iv)(GLint location, GLsizei count, const GLint *value);
void (GL_APIENTRY *UniformMatrix2fv)(GLint, GLsizei, GLboolean, const GLfloat *);
void (GL_APIENTRY *UniformMatrix3fv)(GLint, GLsizei, GLboolean, const GLfloat *);
void (GL_APIENTRY *UniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void (GL_APIENTRY *BlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
// Before using the following members, check null ptr first because they are not valid everywhere
// ES3.1
void (GL_APIENTRY *GetTexLevelParameteriv)(GLenum, GLint, GLenum, GLint *);
#if defined(Q_OS_WIN32)
//#include <GL/wglext.h> //not found in vs2013
//https://www.opengl.org/registry/specs/NV/DX_interop.txt
#ifndef WGL_ACCESS_READ_ONLY_NV
#define WGL_ACCESS_READ_ONLY_NV 0x00000000
#define WGL_ACCESS_READ_WRITE_NV 0x00000001
#define WGL_ACCESS_WRITE_DISCARD_NV 0x00000002
#endif
BOOL (WINAPI* DXSetResourceShareHandleNV)(void *dxObject, HANDLE shareHandle);
HANDLE (WINAPI* DXOpenDeviceNV)(void *dxDevice);
BOOL (WINAPI* DXCloseDeviceNV)(HANDLE hDevice);
HANDLE (WINAPI* DXRegisterObjectNV)(HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access);
BOOL (WINAPI* DXUnregisterObjectNV)(HANDLE hDevice, HANDLE hObject);
BOOL (WINAPI* DXObjectAccessNV)(HANDLE hObject, GLenum access);
BOOL (WINAPI* DXLockObjectsNV)(HANDLE hDevice, GLint count, HANDLE *hObjects);
BOOL (WINAPI* DXUnlockObjectsNV)(HANDLE hDevice, GLint count, HANDLE *hObjects);
#endif
};
} //namespace FAV
#endif //QT_NO_OPENGL
#endif //QTAV_GL_API_H