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