rhi: gl: Add support for arrays of float, vec2, vec3 and vec4

This is a thing in Qt Quick: there are some types of image particles
that use uniforms like "float opacitytable[64]".

This should make all views work correctly in the Image Particles example
when running on the OpenGL backend of QRhi. (other backends should work
as expected already)

Change-Id: I64a04fbb98b97d81d257b00b428582e751d46b8e
Fixes: QTBUG-80667
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
bb10
Laszlo Agocs 2019-12-11 14:13:03 +01:00
parent 5612f24dc5
commit 83818431e1
2 changed files with 70 additions and 10 deletions

View File

@ -2378,12 +2378,23 @@ void QRhiGles2::executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps)
f->glUseProgram(psD->program);
}
static inline void qrhi_std140_to_packed(float *dst, int vecSize, int elemCount, const void *src)
{
const float *p = reinterpret_cast<const float *>(src);
for (int i = 0; i < elemCount; ++i) {
for (int j = 0; j < vecSize; ++j)
dst[vecSize * i + j] = *p++;
p += 4 - vecSize;
}
}
void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
QRhiShaderResourceBindings *srb,
const uint *dynOfsPairs, int dynOfsCount)
{
QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
int texUnit = 0;
QVarLengthArray<float, 256> packedFloatArray;
for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data();
@ -2411,18 +2422,64 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
// so this should not cause unaligned reads
const void *src = bufView.constData() + uniform.offset;
if (uniform.arrayDim > 0
&& uniform.type != QShaderDescription::Float
&& uniform.type != QShaderDescription::Vec2
&& uniform.type != QShaderDescription::Vec3
&& uniform.type != QShaderDescription::Vec4)
{
qWarning("Uniform with buffer binding %d, buffer offset %d, type %d is an array, "
"but arrays are only supported for float, vec2, vec3, and vec4. "
"Only the first element will be set.",
uniform.binding, uniform.offset, uniform.type);
}
// Our input is an std140 layout uniform block. See
// "Standard Uniform Block Layout" in section 7.6.2.2 of
// the OpenGL spec. This has some peculiar alignment
// requirements, which is not what glUniform* wants. Hence
// the unpacking/repacking for arrays and certain types.
switch (uniform.type) {
case QShaderDescription::Float:
f->glUniform1f(uniform.glslLocation, *reinterpret_cast<const float *>(src));
{
const int elemCount = uniform.arrayDim;
if (elemCount < 1) {
f->glUniform1f(uniform.glslLocation, *reinterpret_cast<const float *>(src));
} else {
// input is 16 bytes per element as per std140, have to convert to packed
packedFloatArray.resize(elemCount);
qrhi_std140_to_packed(packedFloatArray.data(), 1, elemCount, src);
f->glUniform1fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
}
}
break;
case QShaderDescription::Vec2:
f->glUniform2fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
{
const int elemCount = uniform.arrayDim;
if (elemCount < 1) {
f->glUniform2fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
} else {
packedFloatArray.resize(elemCount * 2);
qrhi_std140_to_packed(packedFloatArray.data(), 2, elemCount, src);
f->glUniform2fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
}
}
break;
case QShaderDescription::Vec3:
f->glUniform3fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
{
const int elemCount = uniform.arrayDim;
if (elemCount < 1) {
f->glUniform3fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
} else {
packedFloatArray.resize(elemCount * 3);
qrhi_std140_to_packed(packedFloatArray.data(), 3, elemCount, src);
f->glUniform3fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
}
}
break;
case QShaderDescription::Vec4:
f->glUniform4fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
f->glUniform4fv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const float *>(src));
break;
case QShaderDescription::Mat2:
f->glUniformMatrix2fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
@ -2477,8 +2534,9 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
case QShaderDescription::Bool4:
f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
break;
// ### more types
default:
qWarning("Uniform with buffer binding %d, buffer offset %d has unsupported type %d",
uniform.binding, uniform.offset, uniform.type);
break;
}
}
@ -2944,9 +3002,15 @@ void QRhiGles2::registerUniformIfActive(const QShaderDescription::BlockVariable
const QByteArray name = namePrefix + var.name.toUtf8();
uniform.glslLocation = f->glGetUniformLocation(program, name.constData());
if (uniform.glslLocation >= 0) {
if (var.arrayDims.count() > 1) {
qWarning("Array '%s' has more than one dimension. This is not supported.",
qPrintable(var.name));
return;
}
uniform.binding = binding;
uniform.offset = uint(baseOffset + var.offset);
uniform.size = var.size;
uniform.arrayDim = var.arrayDims.isEmpty() ? 0 : var.arrayDims.first();
dst->append(uniform);
}
}
@ -2979,11 +3043,6 @@ void QRhiGles2::gatherUniforms(GLuint program,
}
}
} else {
if (!blockMember.arrayDims.isEmpty()) {
qWarning("Arrays are only supported for structs at the moment. '%s' ignored.",
qPrintable(blockMember.name));
continue;
}
registerUniformIfActive(blockMember, prefix, ub.binding, 0, program, dst);
}
}

View File

@ -252,6 +252,7 @@ struct QGles2UniformDescription
int binding;
uint offset;
int size;
int arrayDim;
};
Q_DECLARE_TYPEINFO(QGles2UniformDescription, Q_MOVABLE_TYPE);