summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/rhi')
-rw-r--r--src/gui/rhi/qrhigles2.cpp79
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h1
-rw-r--r--src/gui/rhi/qrhimetal.mm104
-rw-r--r--src/gui/rhi/qshader.cpp7
4 files changed, 151 insertions, 40 deletions
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 3fb2ec38a7..ffaccbad71 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -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);
}
}
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index 4a98011d3d..d4f1336c3e 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -252,6 +252,7 @@ struct QGles2UniformDescription
int binding;
uint offset;
int size;
+ int arrayDim;
};
Q_DECLARE_TYPEINFO(QGles2UniformDescription, Q_MOVABLE_TYPE);
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 3aa68db585..b6ca40e08b 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -673,12 +673,17 @@ static inline int mapBinding(int binding,
BindingType type)
{
const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
- if (map) {
- auto it = map->constFind(binding);
- if (it != map->cend())
- return type == BindingType::Sampler ? it->second : it->first;
- }
- return binding;
+ if (!map)
+ return binding; // old QShader versions do not have this map, assume 1:1 mapping then
+
+ auto it = map->constFind(binding);
+ if (it != map->cend())
+ return type == BindingType::Sampler ? it->second : it->first; // may be -1, if the resource is inactive
+
+ // Hitting this path is normal too, is not given that the resource (e.g. a
+ // uniform block) is really present in the shaders for all the stages
+ // specified by the visibility mask in the QRhiShaderResourceBinding.
+ return -1;
}
void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
@@ -712,16 +717,25 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
}
}
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- res[VERTEX].buffers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
- res[VERTEX].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[VERTEX].buffers.feed(nativeBinding, mtlbuf);
+ res[VERTEX].bufferOffsets.feed(b->binding, offset);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- res[FRAGMENT].buffers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
- res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[FRAGMENT].buffers.feed(nativeBinding, mtlbuf);
+ res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- res[COMPUTE].buffers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
- res[COMPUTE].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[COMPUTE].buffers.feed(nativeBinding, mtlbuf);
+ res[COMPUTE].bufferOffsets.feed(b->binding, offset);
+ }
}
}
break;
@@ -730,16 +744,28 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- res[VERTEX].textures.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
- res[VERTEX].samplers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
+ const int nativeBindingTexture = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
+ const int nativeBindingSampler = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler);
+ if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
+ res[VERTEX].textures.feed(nativeBindingTexture, texD->d->tex);
+ res[VERTEX].samplers.feed(nativeBindingSampler, samplerD->d->samplerState);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- res[FRAGMENT].textures.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
- res[FRAGMENT].samplers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
+ const int nativeBindingTexture = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
+ const int nativeBindingSampler = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler);
+ if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
+ res[FRAGMENT].textures.feed(nativeBindingTexture, texD->d->tex);
+ res[FRAGMENT].samplers.feed(nativeBindingSampler, samplerD->d->samplerState);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- res[COMPUTE].textures.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
- res[COMPUTE].samplers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
+ const int nativeBindingTexture = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
+ const int nativeBindingSampler = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler);
+ if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
+ res[COMPUTE].textures.feed(nativeBindingTexture, texD->d->tex);
+ res[COMPUTE].samplers.feed(nativeBindingSampler, samplerD->d->samplerState);
+ }
}
}
break;
@@ -751,12 +777,21 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
{
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
- if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage))
- res[VERTEX].textures.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture), t);
- if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
- res[FRAGMENT].textures.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture), t);
- if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
- res[COMPUTE].textures.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture), t);
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
+ if (nativeBinding >= 0)
+ res[VERTEX].textures.feed(nativeBinding, t);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
+ if (nativeBinding >= 0)
+ res[FRAGMENT].textures.feed(nativeBinding, t);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
+ if (nativeBinding >= 0)
+ res[COMPUTE].textures.feed(nativeBinding, t);
+ }
}
break;
case QRhiShaderResourceBinding::BufferLoad:
@@ -769,16 +804,25 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
id<MTLBuffer> mtlbuf = bufD->d->buf[0];
uint offset = uint(b->u.sbuf.offset);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- res[VERTEX].buffers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
- res[VERTEX].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[VERTEX].buffers.feed(nativeBinding, mtlbuf);
+ res[VERTEX].bufferOffsets.feed(b->binding, offset);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- res[FRAGMENT].buffers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
- res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[FRAGMENT].buffers.feed(nativeBinding, mtlbuf);
+ res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- res[COMPUTE].buffers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
- res[COMPUTE].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[COMPUTE].buffers.feed(nativeBinding, mtlbuf);
+ res[COMPUTE].bufferOffsets.feed(b->binding, offset);
+ }
}
}
break;
diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp
index dc6060f882..0b99281f08 100644
--- a/src/gui/rhi/qshader.cpp
+++ b/src/gui/rhi/qshader.cpp
@@ -660,6 +660,13 @@ QDebug operator<<(QDebug dbg, const QShaderVersion &v)
pair, because combined image samplers may map to two native resources (a
texture and a sampler) in some shading languages. In that case the second
value refers to the sampler.
+
+ \note The native binding may be -1, in case there is no active binding for
+ the resource in the shader. (for example, there is a uniform block
+ declared, but it is not used in the shader code) The map is always
+ complete, meaning there is an entry for all declared uniform blocks,
+ storage blocks, image objects, and combined samplers, but the value will be
+ -1 for those that are not actually referenced in the shader functions.
*/
/*!