1 /******************************************************************************
2     QtAV:  Multimedia framework based on Qt and FFmpeg
3     Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
4 
5 *   This file is part of QtAV (from 2016)
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 ******************************************************************************/
21 #include "QtAV/OpenGLTypes.h"
22 #include "opengl/OpenGLHelper.h"
23 #include <QtCore/QRegExp>
24 #include <QtCore/QStringList>
25 #include <QtCore/QVariant>
26 #include "utils/Logger.h"
27 namespace QtAV {
28 struct uniform_type_name {
29     QByteArray name;
30     Uniform::Type type;
31 } uniform_type_names[] ={
32     {"sample2D", Uniform::Sampler},
33     {"bool", Uniform::Bool},
34     {"int", Uniform::Int},
35     {"uint", Uniform::Int},
36     {"float", Uniform::Float},
37     {"vec2", Uniform::Vec2},
38     {"vec3", Uniform::Vec3},
39     {"vec4", Uniform::Vec4},
40     {"mat2", Uniform::Mat2},
41     {"mat3", Uniform::Mat3},
42     {"mat4", Uniform::Mat4},
43     {"bvec2", Uniform::BVec2},
44     {"bvec3", Uniform::BVec3},
45     {"bvec4", Uniform::BVec4},
46     {"ivec2", Uniform::IVec2},
47     {"ivec3", Uniform::IVec3},
48     {"ivec4", Uniform::IVec4},
49     {"uvec2", Uniform::UVec2},
50     {"uvec3", Uniform::UVec3},
51     {"uvec4", Uniform::UVec4},
52     {"mat2x2", Uniform::Mat2},
53     {"mat3x3", Uniform::Mat3},
54     {"mat4x4", Uniform::Mat4},
55     {"dmat2", Uniform::DMat2},
56     {"dmat3", Uniform::DMat3},
57     {"dmat4", Uniform::DMat4},
58 };
59 
UniformTypeFromName(const QByteArray & name)60 static Uniform::Type UniformTypeFromName(const QByteArray& name)
61 {
62     for (const uniform_type_name* un = uniform_type_names; un < uniform_type_names + sizeof(uniform_type_names)/sizeof(uniform_type_names[0]); ++un) {
63         if (un->name == name)
64             return un->type;
65     }
66     return Uniform::Unknown;
67 }
68 
UniformTypeToName(Uniform::Type ut)69 static QByteArray UniformTypeToName(Uniform::Type ut)
70 {
71     for (const uniform_type_name* un = uniform_type_names; un < uniform_type_names + sizeof(uniform_type_names)/sizeof(uniform_type_names[0]); ++un) {
72         if (un->type == ut)
73             return un->name;
74     }
75     return "unknown";
76 }
77 
Uniform(Type tp,int count)78 Uniform::Uniform(Type tp, int count)
79     : dirty(true)
80     , location(-1)
81     , tuple_size(1)
82     , array_size(1)
83     , t(tp)
84 {
85     setType(tp, count);
86 }
87 
setType(Type tp,int count)88 Uniform& Uniform::setType(Type tp, int count)
89 {
90     t = tp;
91     array_size = count;
92     if (isVec()) {
93         tuple_size = (t >> (V+1)) & ((1<<3) - 1);
94     } else if (isMat()) {
95         tuple_size = (t >> (M+1)) & ((1<<3) - 1);
96         tuple_size *= tuple_size;
97     }
98     int element_size = sizeof(float);
99     if (isInt() || isUInt() || isBool()) {
100         element_size = sizeof(int);
101     }
102     data = QVector<int>(element_size/sizeof(int)*tupleSize()*arraySize());
103     return *this;
104 }
105 
set_uniform_value(QVector<int> & dst,const T * v,int count)106 template<typename T> bool set_uniform_value(QVector<int>& dst, const T* v, int count)
107 {
108     Q_ASSERT(sizeof(T)*count <= sizeof(int)*dst.size() && "set_uniform_value: Bad type or array size");
109     // why not dst.constData()?
110     const QVector<int> old(dst);
111     memcpy((char*)dst.data(), (const char*)v, count*sizeof(T));
112     return old != dst;
113 }
114 
set_uniform_value(QVector<int> & dst,const bool * v,int count)115 template<> bool set_uniform_value<bool>(QVector<int>& dst, const bool* v, int count)
116 {
117     const QVector<int> old(dst);
118     for (int i = 0; i < count; ++i) {
119         dst[i] = *(v + i);
120     }
121     return old != dst;
122 }
123 
set(const float & v,int count)124 void Uniform::set(const float &v, int count)
125 {
126     if (count <= 0)
127         count = tupleSize()*arraySize();
128     dirty = set_uniform_value(data, &v, count);
129 }
130 
set(const int & v,int count)131 void Uniform::set(const int &v, int count)
132 {
133     if (count <= 0)
134         count = tupleSize()*arraySize();
135     dirty = set_uniform_value(data, &v, count);
136 
137 }
138 
set(const unsigned & v,int count)139 void Uniform::set(const unsigned &v, int count)
140 {
141     if (count <= 0)
142         count = tupleSize()*arraySize();
143     dirty = set_uniform_value(data, &v, count);
144 }
145 
146 
set(const float * v,int count)147 void Uniform::set(const float *v, int count)
148 {
149     if (count <= 0)
150         count = tupleSize()*arraySize();
151     dirty = set_uniform_value(data, v, count);
152 }
153 
set(const int * v,int count)154 void Uniform::set(const int *v, int count)
155 {
156     if (count <= 0)
157         count = tupleSize()*arraySize();
158     dirty = set_uniform_value(data, v, count);
159 }
160 
set(const unsigned * v,int count)161 void Uniform::set(const unsigned *v, int count)
162 {
163     if (count <= 0)
164         count = tupleSize()*arraySize();
165     dirty = set_uniform_value(data, v, count);
166 }
167 
set(const QVariant & v)168 void Uniform::set(const QVariant &v)
169 {
170     if (tupleSize() > 1 || arraySize() > 1) {
171         if (isFloat()) { //TODO: what if QVector<qreal> but uniform is float?
172             set(v.value<QVector<float> >().data());
173         } else if (isInt() || isBool()) {
174             set(v.value<QVector<int> >().data());
175         } else if (isUInt()) {
176             set(v.value<QVector<unsigned> >().data());
177         } else if (type() == Uniform::Sampler) {
178 
179         }
180     } else {
181         if (isFloat()) {
182             set(v.toFloat());
183         } else if (isInt() || isBool()) {
184             set(v.toInt());
185         } else if (isUInt()) {
186             set(v.toUInt());
187         } else if (type() == Uniform::Sampler) {
188 
189         }
190     }
191 }
192 
setGL()193 bool Uniform::setGL()
194 {
195     if (location < 0) {
196         return false;
197     }
198     switch (type()) {
199     case Uniform::Bool:
200     case Uniform::Int:
201         gl().Uniform1iv(location, arraySize(), address<int>());
202         break;
203     case Uniform::Float:
204         gl().Uniform1fv(location, arraySize(), address<float>());
205         break;
206     case Uniform::Vec2:
207         gl().Uniform2fv(location, arraySize(), address<float>());
208         break;
209     case Uniform::Vec3:
210         gl().Uniform3fv(location, arraySize(), address<float>());
211         break;
212     case Uniform::Vec4:
213         gl().Uniform4fv(location, arraySize(), address<float>());
214         break;
215     case Uniform::Mat2:
216         gl().UniformMatrix2fv(location, arraySize(), GL_FALSE, address<float>());
217         break;
218     case Uniform::Mat3:
219         gl().UniformMatrix3fv(location, arraySize(), GL_FALSE, address<float>());
220         break;
221     case Uniform::Mat4:
222         gl().UniformMatrix4fv(location, arraySize(), GL_FALSE, address<float>());
223         break;
224     case Uniform::IVec2:
225         gl().Uniform2iv(location, arraySize(), address<int>());
226         break;
227     case Uniform::IVec3:
228         gl().Uniform3iv(location, arraySize(), address<int>());
229         break;
230     case Uniform::IVec4:
231         gl().Uniform4iv(location, arraySize(), address<int>());
232         break;
233     default:
234         qDebug() << *this;
235         qWarning("Unsupported uniform type in Qt. You should use 'VideoShader::setUserUniformValues()' to call glUniformXXX or directly call glUniformXXX instead");
236         return false;
237     }
238     dirty = false;
239     return true;
240 }
241 
242 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug dbg,const Uniform & u)243 Q_AV_EXPORT QDebug operator<<(QDebug dbg, const Uniform &u)
244 {
245     dbg.nospace() << "uniform " << UniformTypeToName(u.type()) << " " << u.name.constData();
246     if (u.arraySize() > 1) {
247         dbg.nospace() << "[" << u.arraySize() << "]";
248     }
249     dbg.nospace() << ", dirty: " << u.dirty;
250     dbg.nospace() << ", location: " << u.location << ", " << "tupleSize: " << u.tupleSize() << ", ";
251     if (u.isBool() || u.isInt()) {
252         dbg.nospace() << "value: " << u.value<int>();
253     } else if (u.isUInt()) {
254         dbg.nospace() << "value: " << u.value<unsigned>();
255     } else if (u.isDouble()) {
256         dbg.nospace() << "value: " << u.value<double>();
257     } else {
258         dbg.nospace() << "value: " << u.value<float>();
259     }
260     return dbg.space();
261 }
262 
263 Q_AV_EXPORT QDebug operator<<(QDebug dbg, Uniform::Type ut);
264 #endif
265 
ParseUniforms(const QByteArray & text,GLuint programId=0)266 QVector<Uniform> ParseUniforms(const QByteArray &text, GLuint programId = 0)
267 {
268     QVector<Uniform> uniforms;
269     const QString code = OpenGLHelper::removeComments(QString(text));
270     const QStringList lines = code.split(';');
271     // TODO: highp lowp etc.
272     const QString exp(QStringLiteral("\\s*uniform\\s+([\\w\\d]+)\\s+([\\w\\d]+)\\s*"));
273     const QString exp_array = exp + QStringLiteral("\\[(\\d+)\\]\\s*");
274     foreach (QString line, lines) {
275         line = line.trimmed();
276         if (!line.startsWith(QStringLiteral("uniform ")))
277             continue;
278         QRegExp rx(exp_array);
279         if (rx.indexIn(line) < 0) {
280             rx = QRegExp(exp);
281             if (rx.indexIn(line) < 0)
282                 continue;
283         }
284         Uniform u;
285         const QStringList x = rx.capturedTexts();
286         //qDebug() << x;
287         u.name = x.at(2).toUtf8();
288         int array_size = 1;
289         if (x.size() > 3)
290             array_size = x[3].toInt();
291         const QByteArray t(x[1].toLatin1());
292         u.setType(UniformTypeFromName(t), array_size);
293         if (programId > 0)
294             u.location = gl().GetUniformLocation(programId, u.name.constData());
295         uniforms.append(u);
296     }
297     qDebug() << uniforms;
298     return uniforms;
299 }
300 } //namespace QtAV
301