summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2020-03-03 14:24:11 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2020-03-05 19:40:41 +0100
commitc3ae30085e4047ee7d5a945c36e2055c2de5197d (patch)
tree4a4e8c2b3ef2a3a9353965d4c49832765f785593 /src
parente1e0862990dbe00462f7faa02845a640a3d84155 (diff)
rhi: Add support for arrays of combined image samplers
Introduces a new QRhiShaderResourceBinding function that takes an array of texture-sampler pairs. The existing function is also available and is equivalent to calling the array-based version with array size 1. It is important to note that for Metal one needs MSL 2.0 for array of textures, so qsb needs --msl 20 instead of --msl 12 for such shaders. Comes with an autotest, and also updates all .qsb files for said test with the latest shadertools. Task-number: QTBUG-82624 Change-Id: Ibc1973aae826836f16d842c41d6c8403fd7ff876 Reviewed-by: Christian Strømme <christian.stromme@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/gui/rhi/qrhi.cpp66
-rw-r--r--src/gui/rhi/qrhi_p.h11
-rw-r--r--src/gui/rhi/qrhid3d11.cpp92
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h11
-rw-r--r--src/gui/rhi/qrhigles2.cpp61
-rw-r--r--src/gui/rhi/qrhimetal.mm112
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h11
-rw-r--r--src/gui/rhi/qrhivulkan.cpp83
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h11
9 files changed, 290 insertions, 168 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index c805e23ad0..83c1e8eaa2 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -2885,16 +2885,57 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOff
\return a shader resource binding for the given binding number, pipeline
stages, texture, and sampler specified by \a binding, \a stage, \a tex,
\a sampler.
+
+ \note This function is equivalent to calling sampledTextures() with a
+ \c count of 1.
+
+ \sa sampledTextures()
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture(
int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
{
+ const TextureAndSampler texSampler = { tex, sampler };
+ return sampledTextures(binding, stage, 1, &texSampler);
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, and the array of texture-sampler pairs specified by \a binding, \a
+ stage, \a count, and \a texSamplers.
+
+ \note \a count must be at least 1, and not larger than 16.
+
+ \note When \a count is 1, this function is equivalent to sampledTexture().
+
+ This function is relevant when arrays of combined image samplers are
+ involved. For example, in GLSL \c{layout(binding = 5) uniform sampler2D
+ shadowMaps[8];} declares an array of combined image samplers. The
+ application is then expected provide a QRhiShaderResourceBinding for
+ binding point 5, set up by calling this function with \a count set to 8 and
+ a valid texture and sampler for each element of the array.
+
+ \warning All elements of the array must be specified. With the above
+ example, the only valid, portable approach is calling this function with a
+ \a count of 8. Additionally, all QRhiTexture and QRhiSampler instances must
+ be valid, meaning nullptr is not an accepted value. This is due to some of
+ the underlying APIs, such as, Vulkan, that require a valid image and
+ sampler object for each element in descriptor arrays. Applications are
+ advised to provide "dummy" samplers and textures if some array elements are
+ not relevant (due to not being accessed in the shader).
+
+ \sa sampledTexture()
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTextures(
+ int binding, StageFlags stage, int count, const TextureAndSampler *texSamplers)
+{
+ Q_ASSERT(count >= 1 && count <= Data::MAX_TEX_SAMPLER_ARRAY_SIZE);
QRhiShaderResourceBinding b;
b.d.binding = binding;
b.d.stage = stage;
b.d.type = SampledTexture;
- b.d.u.stex.tex = tex;
- b.d.u.stex.sampler = sampler;
+ b.d.u.stex.count = count;
+ for (int i = 0; i < count; ++i)
+ b.d.u.stex.texSamplers[i] = texSamplers[i];
return b;
}
@@ -3084,10 +3125,14 @@ bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBind
}
break;
case QRhiShaderResourceBinding::SampledTexture:
- if (da->u.stex.tex != db->u.stex.tex
- || da->u.stex.sampler != db->u.stex.sampler)
- {
+ if (da->u.stex.count != db->u.stex.count)
return false;
+ for (int i = 0; i < da->u.stex.count; ++i) {
+ if (da->u.stex.texSamplers[i].tex != db->u.stex.texSamplers[i].tex
+ || da->u.stex.texSamplers[i].sampler != db->u.stex.texSamplers[i].sampler)
+ {
+ return false;
+ }
}
break;
case QRhiShaderResourceBinding::ImageLoad:
@@ -3162,10 +3207,13 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b)
<< ')';
break;
case QRhiShaderResourceBinding::SampledTexture:
- dbg.nospace() << " SampledTexture("
- << "texture=" << d->u.stex.tex
- << " sampler=" << d->u.stex.sampler
- << ')';
+ dbg.nospace() << " SampledTextures("
+ << "count=" << d->u.stex.count;
+ for (int i = 0; i < d->u.stex.count; ++i) {
+ dbg.nospace() << " texture=" << d->u.stex.texSamplers[i].tex
+ << " sampler=" << d->u.stex.texSamplers[i].sampler;
+ }
+ dbg.nospace() << ')';
break;
case QRhiShaderResourceBinding::ImageLoad:
dbg.nospace() << " ImageLoad("
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index 17c911a5ff..9d906d7bbd 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -348,6 +348,12 @@ public:
static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler);
+ struct TextureAndSampler {
+ QRhiTexture *tex;
+ QRhiSampler *sampler;
+ };
+ static QRhiShaderResourceBinding sampledTextures(int binding, StageFlags stage, int count, const TextureAndSampler *texSamplers);
+
static QRhiShaderResourceBinding imageLoad(int binding, StageFlags stage, QRhiTexture *tex, int level);
static QRhiShaderResourceBinding imageStore(int binding, StageFlags stage, QRhiTexture *tex, int level);
static QRhiShaderResourceBinding imageLoadStore(int binding, StageFlags stage, QRhiTexture *tex, int level);
@@ -370,9 +376,10 @@ public:
int maybeSize;
bool hasDynamicOffset;
};
+ static const int MAX_TEX_SAMPLER_ARRAY_SIZE = 16;
struct SampledTextureData {
- QRhiTexture *tex;
- QRhiSampler *sampler;
+ int count;
+ TextureAndSampler texSamplers[MAX_TEX_SAMPLER_ARRAY_SIZE];
};
struct StorageImageData {
QRhiTexture *tex;
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 7b583e6fd2..7c53925aca 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -627,18 +627,25 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
break;
case QRhiShaderResourceBinding::SampledTexture:
{
- QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex);
- QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler);
- if (texD->generation != bd.stex.texGeneration
- || texD->m_id != bd.stex.texId
- || samplerD->generation != bd.stex.samplerGeneration
- || samplerD->m_id != bd.stex.samplerId)
- {
+ const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
+ if (bd.stex.count != data->count) {
+ bd.stex.count = data->count;
srbUpdate = true;
- bd.stex.texId = texD->m_id;
- bd.stex.texGeneration = texD->generation;
- bd.stex.samplerId = samplerD->m_id;
- bd.stex.samplerGeneration = samplerD->generation;
+ }
+ for (int elem = 0; elem < data->count; ++elem) {
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex);
+ QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler);
+ if (texD->generation != bd.stex.d[elem].texGeneration
+ || texD->m_id != bd.stex.d[elem].texId
+ || samplerD->generation != bd.stex.d[elem].samplerGeneration
+ || samplerD->m_id != bd.stex.d[elem].samplerId)
+ {
+ srbUpdate = true;
+ bd.stex.d[elem].texId = texD->m_id;
+ bd.stex.d[elem].texGeneration = texD->generation;
+ bd.stex.d[elem].samplerId = samplerD->m_id;
+ bd.stex.d[elem].samplerGeneration = samplerD->generation;
+ }
}
}
break;
@@ -1894,31 +1901,38 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
break;
case QRhiShaderResourceBinding::SampledTexture:
{
- QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex);
- QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler);
- bd.stex.texId = texD->m_id;
- bd.stex.texGeneration = texD->generation;
- bd.stex.samplerId = samplerD->m_id;
- bd.stex.samplerGeneration = samplerD->generation;
- if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
- if (nativeBinding.first >= 0 && nativeBinding.second >= 0) {
- res[RBM_VERTEX].textures.append({ nativeBinding.first, texD->srv });
- res[RBM_VERTEX].samplers.append({ nativeBinding.second, samplerD->samplerState });
+ const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
+ bd.stex.count = data->count;
+ const QPair<int, int> nativeBindingVert = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
+ const QPair<int, int> nativeBindingFrag = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
+ const QPair<int, int> nativeBindingComp = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
+ // if SPIR-V binding b is mapped to tN and sN in HLSL, and it
+ // is an array, then it will use tN, tN+1, tN+2, ..., and sN,
+ // sN+1, sN+2, ...
+ for (int elem = 0; elem < data->count; ++elem) {
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex);
+ QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler);
+ bd.stex.d[elem].texId = texD->m_id;
+ bd.stex.d[elem].texGeneration = texD->generation;
+ bd.stex.d[elem].samplerId = samplerD->m_id;
+ bd.stex.d[elem].samplerGeneration = samplerD->generation;
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ if (nativeBindingVert.first >= 0 && nativeBindingVert.second >= 0) {
+ res[RBM_VERTEX].textures.append({ nativeBindingVert.first + elem, texD->srv });
+ res[RBM_VERTEX].samplers.append({ nativeBindingVert.second + elem, samplerD->samplerState });
+ }
}
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
- if (nativeBinding.first >= 0 && nativeBinding.second >= 0) {
- res[RBM_FRAGMENT].textures.append({ nativeBinding.first, texD->srv });
- res[RBM_FRAGMENT].samplers.append({ nativeBinding.second, samplerD->samplerState });
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ if (nativeBindingFrag.first >= 0 && nativeBindingFrag.second >= 0) {
+ res[RBM_FRAGMENT].textures.append({ nativeBindingFrag.first + elem, texD->srv });
+ res[RBM_FRAGMENT].samplers.append({ nativeBindingFrag.second + elem, samplerD->samplerState });
+ }
}
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
- if (nativeBinding.first >= 0 && nativeBinding.second >= 0) {
- res[RBM_COMPUTE].textures.append({ nativeBinding.first, texD->srv });
- res[RBM_COMPUTE].samplers.append({ nativeBinding.second, samplerD->samplerState });
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ if (nativeBindingComp.first >= 0 && nativeBindingComp.second >= 0) {
+ res[RBM_COMPUTE].textures.append({ nativeBindingComp.first + elem, texD->srv });
+ res[RBM_COMPUTE].samplers.append({ nativeBindingComp.second + elem, samplerD->samplerState });
+ }
}
}
}
@@ -3529,11 +3543,15 @@ static pD3DCompile resolveD3DCompile()
static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, QString *error, QShaderKey *usedShaderKey)
{
- QShaderCode dxbc = shader.shader({ QShader::DxbcShader, 50, shaderVariant });
- if (!dxbc.shader().isEmpty())
+ QShaderKey key = { QShader::DxbcShader, 50, shaderVariant };
+ QShaderCode dxbc = shader.shader(key);
+ if (!dxbc.shader().isEmpty()) {
+ if (usedShaderKey)
+ *usedShaderKey = key;
return dxbc.shader();
+ }
- const QShaderKey key = { QShader::HlslShader, 50, shaderVariant };
+ key = { QShader::HlslShader, 50, shaderVariant };
QShaderCode hlslSource = shader.shader(key);
if (hlslSource.shader().isEmpty()) {
qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader;
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index f749b612b5..33412b8011 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -210,10 +210,13 @@ struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings
uint generation;
};
struct BoundSampledTextureData {
- quint64 texId;
- uint texGeneration;
- quint64 samplerId;
- uint samplerGeneration;
+ int count;
+ struct {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
};
struct BoundStorageImageData {
quint64 id;
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index feeb65137a..3ba83464d2 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -919,10 +919,12 @@ void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
hasDynamicOffsetInSrb = true;
break;
case QRhiShaderResourceBinding::SampledTexture:
- trackedRegisterTexture(&passResTracker,
- QRHI_RES(QGles2Texture, b->u.stex.tex),
- QRhiPassResourceTracker::TexSample,
- QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
+ for (int elem = 0; elem < b->u.stex.count; ++elem) {
+ trackedRegisterTexture(&passResTracker,
+ QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex),
+ QRhiPassResourceTracker::TexSample,
+ QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
+ }
break;
case QRhiShaderResourceBinding::ImageLoad:
case QRhiShaderResourceBinding::ImageStore:
@@ -2574,36 +2576,37 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
break;
case QRhiShaderResourceBinding::SampledTexture:
{
- QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.tex);
- QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.sampler);
QVector<QGles2SamplerDescription> &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
: QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
-
- for (QGles2SamplerDescription &sampler : samplers) {
- if (sampler.binding == b->binding) {
- f->glActiveTexture(GL_TEXTURE0 + uint(texUnit));
- f->glBindTexture(texD->target, texD->texture);
-
- if (texD->samplerState != samplerD->d) {
- f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter));
- f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter));
- f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps));
- f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt));
- // 3D textures not supported by GLES 2.0 or by us atm...
- //f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr);
- if (caps.textureCompareMode) {
- if (samplerD->d.gltexcomparefunc != GL_NEVER) {
- f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc));
- } else {
- f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+ for (int elem = 0; elem < b->u.stex.count; ++elem) {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex);
+ QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.texSamplers[elem].sampler);
+ for (QGles2SamplerDescription &sampler : samplers) {
+ if (sampler.binding == b->binding) {
+ f->glActiveTexture(GL_TEXTURE0 + uint(texUnit));
+ f->glBindTexture(texD->target, texD->texture);
+
+ if (texD->samplerState != samplerD->d) {
+ f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter));
+ f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter));
+ f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps));
+ f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt));
+ // 3D textures not supported by GLES 2.0 or by us atm...
+ //f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr);
+ if (caps.textureCompareMode) {
+ if (samplerD->d.gltexcomparefunc != GL_NEVER) {
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc));
+ } else {
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+ }
}
+ texD->samplerState = samplerD->d;
}
- texD->samplerState = samplerD->d;
- }
- f->glUniform1i(sampler.glslLocation, texUnit);
- ++texUnit;
+ f->glUniform1i(sampler.glslLocation + elem, texUnit);
+ ++texUnit;
+ }
}
}
}
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 314c58b0b7..0806c8a052 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -748,30 +748,33 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
break;
case QRhiShaderResourceBinding::SampledTexture:
{
- QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
- QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
- if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- 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.append({ nativeBindingTexture, texD->d->tex });
- res[VERTEX].samplers.append({ nativeBindingSampler, samplerD->d->samplerState });
+ const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
+ for (int elem = 0; elem < data->count; ++elem) {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
+ QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ 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.append({ nativeBindingTexture + elem, texD->d->tex });
+ res[VERTEX].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState });
+ }
}
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- 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.append({ nativeBindingTexture, texD->d->tex });
- res[FRAGMENT].samplers.append({ nativeBindingSampler, samplerD->d->samplerState });
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ 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.append({ nativeBindingTexture + elem, texD->d->tex });
+ res[FRAGMENT].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState });
+ }
}
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- 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.append({ nativeBindingTexture, texD->d->tex });
- res[COMPUTE].samplers.append({ nativeBindingSampler, samplerD->d->samplerState });
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ 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.append({ nativeBindingTexture + elem, texD->d->tex });
+ res[COMPUTE].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState });
+ }
}
}
}
@@ -1020,21 +1023,28 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
break;
case QRhiShaderResourceBinding::SampledTexture:
{
- QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
- QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
- if (texD->generation != bd.stex.texGeneration
- || texD->m_id != bd.stex.texId
- || samplerD->generation != bd.stex.samplerGeneration
- || samplerD->m_id != bd.stex.samplerId)
- {
+ const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
+ if (bd.stex.count != data->count) {
+ bd.stex.count = data->count;
resNeedsRebind = true;
- bd.stex.texId = texD->m_id;
- bd.stex.texGeneration = texD->generation;
- bd.stex.samplerId = samplerD->m_id;
- bd.stex.samplerGeneration = samplerD->generation;
}
- texD->lastActiveFrameSlot = currentFrameSlot;
- samplerD->lastActiveFrameSlot = currentFrameSlot;
+ for (int elem = 0; elem < data->count; ++elem) {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, data->texSamplers[elem].tex);
+ QMetalSampler *samplerD = QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler);
+ if (texD->generation != bd.stex.d[elem].texGeneration
+ || texD->m_id != bd.stex.d[elem].texId
+ || samplerD->generation != bd.stex.d[elem].samplerGeneration
+ || samplerD->m_id != bd.stex.d[elem].samplerId)
+ {
+ resNeedsRebind = true;
+ bd.stex.d[elem].texId = texD->m_id;
+ bd.stex.d[elem].texGeneration = texD->generation;
+ bd.stex.d[elem].samplerId = samplerD->m_id;
+ bd.stex.d[elem].samplerGeneration = samplerD->generation;
+ }
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ samplerD->lastActiveFrameSlot = currentFrameSlot;
+ }
}
break;
case QRhiShaderResourceBinding::ImageLoad:
@@ -2981,12 +2991,16 @@ bool QMetalShaderResourceBindings::build()
break;
case QRhiShaderResourceBinding::SampledTexture:
{
- QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
- QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
- bd.stex.texId = texD->m_id;
- bd.stex.texGeneration = texD->generation;
- bd.stex.samplerId = samplerD->m_id;
- bd.stex.samplerGeneration = samplerD->generation;
+ const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
+ bd.stex.count = data->count;
+ for (int elem = 0; elem < data->count; ++elem) {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, data->texSamplers[elem].tex);
+ QMetalSampler *samplerD = QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler);
+ bd.stex.d[elem].texId = texD->m_id;
+ bd.stex.d[elem].texGeneration = texD->generation;
+ bd.stex.d[elem].samplerId = samplerD->m_id;
+ bd.stex.d[elem].samplerGeneration = samplerD->generation;
+ }
}
break;
case QRhiShaderResourceBinding::ImageLoad:
@@ -3241,8 +3255,12 @@ static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c)
id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
{
- QShaderKey key = { QShader::MetalLibShader, 12, shaderVariant };
+ QShaderKey key = { QShader::MetalLibShader, 20, shaderVariant };
QShaderCode mtllib = shader.shader(key);
+ if (mtllib.shader().isEmpty()) {
+ key.setSourceVersion(12);
+ mtllib = shader.shader(key);
+ }
if (!mtllib.shader().isEmpty()) {
dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
size_t(mtllib.shader().size()),
@@ -3261,16 +3279,20 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
}
}
- key = { QShader::MslShader, 12, shaderVariant };
+ key = { QShader::MslShader, 20, shaderVariant };
QShaderCode mslSource = shader.shader(key);
if (mslSource.shader().isEmpty()) {
- qWarning() << "No MSL 1.2 code found in baked shader" << shader;
+ key.setSourceVersion(12);
+ mslSource = shader.shader(key);
+ }
+ if (mslSource.shader().isEmpty()) {
+ qWarning() << "No MSL 2.0 or 1.2 code found in baked shader" << shader;
return nil;
}
NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
- opts.languageVersion = MTLLanguageVersion1_2;
+ opts.languageVersion = key.sourceVersion() == 20 ? MTLLanguageVersion2_0 : MTLLanguageVersion1_2;
NSError *err = nil;
id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
[opts release];
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index a5af5611a6..cb4b777d88 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -197,10 +197,13 @@ struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings
uint generation;
};
struct BoundSampledTextureData {
- quint64 texId;
- uint texGeneration;
- quint64 samplerId;
- uint samplerGeneration;
+ int count;
+ struct {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
};
struct BoundStorageImageData {
quint64 id;
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index a92c3e14e9..ca913475a5 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -2487,7 +2487,8 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
QVarLengthArray<VkDescriptorBufferInfo, 8> bufferInfos;
- QVarLengthArray<VkDescriptorImageInfo, 8> imageInfos;
+ using ArrayOfImageDesc = QVarLengthArray<VkDescriptorImageInfo, 8>;
+ QVarLengthArray<ArrayOfImageDesc, 8> imageInfos;
QVarLengthArray<VkWriteDescriptorSet, 12> writeInfos;
QVarLengthArray<QPair<int, int>, 12> infoIndices;
@@ -2530,17 +2531,22 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
break;
case QRhiShaderResourceBinding::SampledTexture:
{
- QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex);
- QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler);
+ const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
+ writeInfo.descriptorCount = data->count; // arrays of combined image samplers are supported
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
- bd.stex.texId = texD->m_id;
- bd.stex.texGeneration = texD->generation;
- bd.stex.samplerId = samplerD->m_id;
- bd.stex.samplerGeneration = samplerD->generation;
- VkDescriptorImageInfo imageInfo;
- imageInfo.sampler = samplerD->sampler;
- imageInfo.imageView = texD->imageView;
- imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ ArrayOfImageDesc imageInfo(data->count);
+ for (int elem = 0; elem < data->count; ++elem) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
+ QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
+ bd.stex.d[elem].texId = texD->m_id;
+ bd.stex.d[elem].texGeneration = texD->generation;
+ bd.stex.d[elem].samplerId = samplerD->m_id;
+ bd.stex.d[elem].samplerGeneration = samplerD->generation;
+ imageInfo[elem].sampler = samplerD->sampler;
+ imageInfo[elem].imageView = texD->imageView;
+ imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ }
+ bd.stex.count = data->count;
imageInfoIndex = imageInfos.count();
imageInfos.append(imageInfo);
}
@@ -2555,10 +2561,10 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
bd.simage.id = texD->m_id;
bd.simage.generation = texD->generation;
- VkDescriptorImageInfo imageInfo;
- imageInfo.sampler = VK_NULL_HANDLE;
- imageInfo.imageView = view;
- imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+ ArrayOfImageDesc imageInfo(1);
+ imageInfo[0].sampler = VK_NULL_HANDLE;
+ imageInfo[0].imageView = view;
+ imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
imageInfoIndex = imageInfos.count();
imageInfos.append(imageInfo);
}
@@ -2596,7 +2602,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
if (bufferInfoIndex >= 0)
writeInfos[i].pBufferInfo = &bufferInfos[bufferInfoIndex];
else if (imageInfoIndex >= 0)
- writeInfos[i].pImageInfo = &imageInfos[imageInfoIndex];
+ writeInfos[i].pImageInfo = imageInfos[imageInfoIndex].constData();
}
df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.count()), writeInfos.constData(), 0, nullptr);
@@ -4210,24 +4216,30 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
break;
case QRhiShaderResourceBinding::SampledTexture:
{
- QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex);
- QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler);
- texD->lastActiveFrameSlot = currentFrameSlot;
- samplerD->lastActiveFrameSlot = currentFrameSlot;
- trackedRegisterTexture(&passResTracker, texD,
- QRhiPassResourceTracker::TexSample,
- QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
-
- if (texD->generation != bd.stex.texGeneration
- || texD->m_id != bd.stex.texId
- || samplerD->generation != bd.stex.samplerGeneration
- || samplerD->m_id != bd.stex.samplerId)
- {
+ const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
+ if (bd.stex.count != data->count) {
+ bd.stex.count = data->count;
rewriteDescSet = true;
- bd.stex.texId = texD->m_id;
- bd.stex.texGeneration = texD->generation;
- bd.stex.samplerId = samplerD->m_id;
- bd.stex.samplerGeneration = samplerD->generation;
+ }
+ for (int elem = 0; elem < data->count; ++elem) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
+ QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ samplerD->lastActiveFrameSlot = currentFrameSlot;
+ trackedRegisterTexture(&passResTracker, texD,
+ QRhiPassResourceTracker::TexSample,
+ QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
+ if (texD->generation != bd.stex.d[elem].texGeneration
+ || texD->m_id != bd.stex.d[elem].texId
+ || samplerD->generation != bd.stex.d[elem].samplerGeneration
+ || samplerD->m_id != bd.stex.d[elem].samplerId)
+ {
+ rewriteDescSet = true;
+ bd.stex.d[elem].texId = texD->m_id;
+ bd.stex.d[elem].texGeneration = texD->generation;
+ bd.stex.d[elem].samplerId = samplerD->m_id;
+ bd.stex.d[elem].samplerGeneration = samplerD->generation;
+ }
}
}
break;
@@ -6065,7 +6077,10 @@ bool QVkShaderResourceBindings::build()
memset(&vkbinding, 0, sizeof(vkbinding));
vkbinding.binding = uint32_t(b->binding);
vkbinding.descriptorType = toVkDescriptorType(b);
- vkbinding.descriptorCount = 1; // no array support yet
+ if (b->type == QRhiShaderResourceBinding::SampledTexture)
+ vkbinding.descriptorCount = b->u.stex.count;
+ else
+ vkbinding.descriptorCount = 1;
vkbinding.stageFlags = toVkShaderStageFlags(b->stage);
vkbindings.append(vkbinding);
}
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
index fd65417e75..62516e268d 100644
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -254,10 +254,13 @@ struct QVkShaderResourceBindings : public QRhiShaderResourceBindings
uint generation;
};
struct BoundSampledTextureData {
- quint64 texId;
- uint texGeneration;
- quint64 samplerId;
- uint samplerGeneration;
+ int count;
+ struct {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
};
struct BoundStorageImageData {
quint64 id;