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>
This commit is contained in:
parent
e1e0862990
commit
c3ae30085e
@ -2885,16 +2885,57 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOff
|
|||||||
\return a shader resource binding for the given binding number, pipeline
|
\return a shader resource binding for the given binding number, pipeline
|
||||||
stages, texture, and sampler specified by \a binding, \a stage, \a tex,
|
stages, texture, and sampler specified by \a binding, \a stage, \a tex,
|
||||||
\a sampler.
|
\a sampler.
|
||||||
|
|
||||||
|
\note This function is equivalent to calling sampledTextures() with a
|
||||||
|
\c count of 1.
|
||||||
|
|
||||||
|
\sa sampledTextures()
|
||||||
*/
|
*/
|
||||||
QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture(
|
QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture(
|
||||||
int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
|
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;
|
QRhiShaderResourceBinding b;
|
||||||
b.d.binding = binding;
|
b.d.binding = binding;
|
||||||
b.d.stage = stage;
|
b.d.stage = stage;
|
||||||
b.d.type = SampledTexture;
|
b.d.type = SampledTexture;
|
||||||
b.d.u.stex.tex = tex;
|
b.d.u.stex.count = count;
|
||||||
b.d.u.stex.sampler = sampler;
|
for (int i = 0; i < count; ++i)
|
||||||
|
b.d.u.stex.texSamplers[i] = texSamplers[i];
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3084,11 +3125,15 @@ bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBind
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
case QRhiShaderResourceBinding::SampledTexture:
|
||||||
if (da->u.stex.tex != db->u.stex.tex
|
if (da->u.stex.count != db->u.stex.count)
|
||||||
|| da->u.stex.sampler != db->u.stex.sampler)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::ImageLoad:
|
case QRhiShaderResourceBinding::ImageLoad:
|
||||||
Q_FALLTHROUGH();
|
Q_FALLTHROUGH();
|
||||||
@ -3162,10 +3207,13 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b)
|
|||||||
<< ')';
|
<< ')';
|
||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
case QRhiShaderResourceBinding::SampledTexture:
|
||||||
dbg.nospace() << " SampledTexture("
|
dbg.nospace() << " SampledTextures("
|
||||||
<< "texture=" << d->u.stex.tex
|
<< "count=" << d->u.stex.count;
|
||||||
<< " sampler=" << d->u.stex.sampler
|
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;
|
break;
|
||||||
case QRhiShaderResourceBinding::ImageLoad:
|
case QRhiShaderResourceBinding::ImageLoad:
|
||||||
dbg.nospace() << " ImageLoad("
|
dbg.nospace() << " ImageLoad("
|
||||||
|
@ -348,6 +348,12 @@ public:
|
|||||||
|
|
||||||
static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler);
|
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 imageLoad(int binding, StageFlags stage, QRhiTexture *tex, int level);
|
||||||
static QRhiShaderResourceBinding imageStore(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);
|
static QRhiShaderResourceBinding imageLoadStore(int binding, StageFlags stage, QRhiTexture *tex, int level);
|
||||||
@ -370,9 +376,10 @@ public:
|
|||||||
int maybeSize;
|
int maybeSize;
|
||||||
bool hasDynamicOffset;
|
bool hasDynamicOffset;
|
||||||
};
|
};
|
||||||
|
static const int MAX_TEX_SAMPLER_ARRAY_SIZE = 16;
|
||||||
struct SampledTextureData {
|
struct SampledTextureData {
|
||||||
QRhiTexture *tex;
|
int count;
|
||||||
QRhiSampler *sampler;
|
TextureAndSampler texSamplers[MAX_TEX_SAMPLER_ARRAY_SIZE];
|
||||||
};
|
};
|
||||||
struct StorageImageData {
|
struct StorageImageData {
|
||||||
QRhiTexture *tex;
|
QRhiTexture *tex;
|
||||||
|
@ -627,18 +627,25 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
|
|||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
case QRhiShaderResourceBinding::SampledTexture:
|
||||||
{
|
{
|
||||||
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex);
|
const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
|
||||||
QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler);
|
if (bd.stex.count != data->count) {
|
||||||
if (texD->generation != bd.stex.texGeneration
|
bd.stex.count = data->count;
|
||||||
|| texD->m_id != bd.stex.texId
|
srbUpdate = true;
|
||||||
|| samplerD->generation != bd.stex.samplerGeneration
|
}
|
||||||
|| samplerD->m_id != bd.stex.samplerId)
|
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;
|
srbUpdate = true;
|
||||||
bd.stex.texId = texD->m_id;
|
bd.stex.d[elem].texId = texD->m_id;
|
||||||
bd.stex.texGeneration = texD->generation;
|
bd.stex.d[elem].texGeneration = texD->generation;
|
||||||
bd.stex.samplerId = samplerD->m_id;
|
bd.stex.d[elem].samplerId = samplerD->m_id;
|
||||||
bd.stex.samplerGeneration = samplerD->generation;
|
bd.stex.d[elem].samplerGeneration = samplerD->generation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1894,31 +1901,38 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
|
|||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
case QRhiShaderResourceBinding::SampledTexture:
|
||||||
{
|
{
|
||||||
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex);
|
const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
|
||||||
QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler);
|
bd.stex.count = data->count;
|
||||||
bd.stex.texId = texD->m_id;
|
const QPair<int, int> nativeBindingVert = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
|
||||||
bd.stex.texGeneration = texD->generation;
|
const QPair<int, int> nativeBindingFrag = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
|
||||||
bd.stex.samplerId = samplerD->m_id;
|
const QPair<int, int> nativeBindingComp = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
|
||||||
bd.stex.samplerGeneration = samplerD->generation;
|
// 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 (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
|
||||||
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
|
if (nativeBindingVert.first >= 0 && nativeBindingVert.second >= 0) {
|
||||||
if (nativeBinding.first >= 0 && nativeBinding.second >= 0) {
|
res[RBM_VERTEX].textures.append({ nativeBindingVert.first + elem, texD->srv });
|
||||||
res[RBM_VERTEX].textures.append({ nativeBinding.first, texD->srv });
|
res[RBM_VERTEX].samplers.append({ nativeBindingVert.second + elem, samplerD->samplerState });
|
||||||
res[RBM_VERTEX].samplers.append({ nativeBinding.second, samplerD->samplerState });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
|
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
|
||||||
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
|
if (nativeBindingFrag.first >= 0 && nativeBindingFrag.second >= 0) {
|
||||||
if (nativeBinding.first >= 0 && nativeBinding.second >= 0) {
|
res[RBM_FRAGMENT].textures.append({ nativeBindingFrag.first + elem, texD->srv });
|
||||||
res[RBM_FRAGMENT].textures.append({ nativeBinding.first, texD->srv });
|
res[RBM_FRAGMENT].samplers.append({ nativeBindingFrag.second + elem, samplerD->samplerState });
|
||||||
res[RBM_FRAGMENT].samplers.append({ nativeBinding.second, samplerD->samplerState });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
|
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
|
||||||
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
|
if (nativeBindingComp.first >= 0 && nativeBindingComp.second >= 0) {
|
||||||
if (nativeBinding.first >= 0 && nativeBinding.second >= 0) {
|
res[RBM_COMPUTE].textures.append({ nativeBindingComp.first + elem, texD->srv });
|
||||||
res[RBM_COMPUTE].textures.append({ nativeBinding.first, texD->srv });
|
res[RBM_COMPUTE].samplers.append({ nativeBindingComp.second + elem, samplerD->samplerState });
|
||||||
res[RBM_COMPUTE].samplers.append({ nativeBinding.second, samplerD->samplerState });
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3529,11 +3543,15 @@ static pD3DCompile resolveD3DCompile()
|
|||||||
|
|
||||||
static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, QString *error, QShaderKey *usedShaderKey)
|
static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, QString *error, QShaderKey *usedShaderKey)
|
||||||
{
|
{
|
||||||
QShaderCode dxbc = shader.shader({ QShader::DxbcShader, 50, shaderVariant });
|
QShaderKey key = { QShader::DxbcShader, 50, shaderVariant };
|
||||||
if (!dxbc.shader().isEmpty())
|
QShaderCode dxbc = shader.shader(key);
|
||||||
|
if (!dxbc.shader().isEmpty()) {
|
||||||
|
if (usedShaderKey)
|
||||||
|
*usedShaderKey = key;
|
||||||
return dxbc.shader();
|
return dxbc.shader();
|
||||||
|
}
|
||||||
|
|
||||||
const QShaderKey key = { QShader::HlslShader, 50, shaderVariant };
|
key = { QShader::HlslShader, 50, shaderVariant };
|
||||||
QShaderCode hlslSource = shader.shader(key);
|
QShaderCode hlslSource = shader.shader(key);
|
||||||
if (hlslSource.shader().isEmpty()) {
|
if (hlslSource.shader().isEmpty()) {
|
||||||
qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader;
|
qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader;
|
||||||
|
@ -210,10 +210,13 @@ struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings
|
|||||||
uint generation;
|
uint generation;
|
||||||
};
|
};
|
||||||
struct BoundSampledTextureData {
|
struct BoundSampledTextureData {
|
||||||
|
int count;
|
||||||
|
struct {
|
||||||
quint64 texId;
|
quint64 texId;
|
||||||
uint texGeneration;
|
uint texGeneration;
|
||||||
quint64 samplerId;
|
quint64 samplerId;
|
||||||
uint samplerGeneration;
|
uint samplerGeneration;
|
||||||
|
} d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
|
||||||
};
|
};
|
||||||
struct BoundStorageImageData {
|
struct BoundStorageImageData {
|
||||||
quint64 id;
|
quint64 id;
|
||||||
|
@ -919,10 +919,12 @@ void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
|
|||||||
hasDynamicOffsetInSrb = true;
|
hasDynamicOffsetInSrb = true;
|
||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
case QRhiShaderResourceBinding::SampledTexture:
|
||||||
|
for (int elem = 0; elem < b->u.stex.count; ++elem) {
|
||||||
trackedRegisterTexture(&passResTracker,
|
trackedRegisterTexture(&passResTracker,
|
||||||
QRHI_RES(QGles2Texture, b->u.stex.tex),
|
QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex),
|
||||||
QRhiPassResourceTracker::TexSample,
|
QRhiPassResourceTracker::TexSample,
|
||||||
QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
|
QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::ImageLoad:
|
case QRhiShaderResourceBinding::ImageLoad:
|
||||||
case QRhiShaderResourceBinding::ImageStore:
|
case QRhiShaderResourceBinding::ImageStore:
|
||||||
@ -2574,11 +2576,11 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
|
|||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
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
|
QVector<QGles2SamplerDescription> &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
|
||||||
: QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
|
: QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
|
||||||
|
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) {
|
for (QGles2SamplerDescription &sampler : samplers) {
|
||||||
if (sampler.binding == b->binding) {
|
if (sampler.binding == b->binding) {
|
||||||
f->glActiveTexture(GL_TEXTURE0 + uint(texUnit));
|
f->glActiveTexture(GL_TEXTURE0 + uint(texUnit));
|
||||||
@ -2602,11 +2604,12 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
|
|||||||
texD->samplerState = samplerD->d;
|
texD->samplerState = samplerD->d;
|
||||||
}
|
}
|
||||||
|
|
||||||
f->glUniform1i(sampler.glslLocation, texUnit);
|
f->glUniform1i(sampler.glslLocation + elem, texUnit);
|
||||||
++texUnit;
|
++texUnit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::ImageLoad:
|
case QRhiShaderResourceBinding::ImageLoad:
|
||||||
case QRhiShaderResourceBinding::ImageStore:
|
case QRhiShaderResourceBinding::ImageStore:
|
||||||
|
@ -748,30 +748,33 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
|
|||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
case QRhiShaderResourceBinding::SampledTexture:
|
||||||
{
|
{
|
||||||
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
|
const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
|
||||||
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
|
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)) {
|
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
|
||||||
const int nativeBindingTexture = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
|
const int nativeBindingTexture = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
|
||||||
const int nativeBindingSampler = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler);
|
const int nativeBindingSampler = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler);
|
||||||
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
||||||
res[VERTEX].textures.append({ nativeBindingTexture, texD->d->tex });
|
res[VERTEX].textures.append({ nativeBindingTexture + elem, texD->d->tex });
|
||||||
res[VERTEX].samplers.append({ nativeBindingSampler, samplerD->d->samplerState });
|
res[VERTEX].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
|
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
|
||||||
const int nativeBindingTexture = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
|
const int nativeBindingTexture = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
|
||||||
const int nativeBindingSampler = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler);
|
const int nativeBindingSampler = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler);
|
||||||
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
||||||
res[FRAGMENT].textures.append({ nativeBindingTexture, texD->d->tex });
|
res[FRAGMENT].textures.append({ nativeBindingTexture + elem, texD->d->tex });
|
||||||
res[FRAGMENT].samplers.append({ nativeBindingSampler, samplerD->d->samplerState });
|
res[FRAGMENT].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
|
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
|
||||||
const int nativeBindingTexture = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
|
const int nativeBindingTexture = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
|
||||||
const int nativeBindingSampler = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler);
|
const int nativeBindingSampler = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler);
|
||||||
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
||||||
res[COMPUTE].textures.append({ nativeBindingTexture, texD->d->tex });
|
res[COMPUTE].textures.append({ nativeBindingTexture + elem, texD->d->tex });
|
||||||
res[COMPUTE].samplers.append({ nativeBindingSampler, samplerD->d->samplerState });
|
res[COMPUTE].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1020,22 +1023,29 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
|
|||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
case QRhiShaderResourceBinding::SampledTexture:
|
||||||
{
|
{
|
||||||
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
|
const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
|
||||||
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
|
if (bd.stex.count != data->count) {
|
||||||
if (texD->generation != bd.stex.texGeneration
|
bd.stex.count = data->count;
|
||||||
|| texD->m_id != bd.stex.texId
|
resNeedsRebind = true;
|
||||||
|| samplerD->generation != bd.stex.samplerGeneration
|
}
|
||||||
|| samplerD->m_id != bd.stex.samplerId)
|
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;
|
resNeedsRebind = true;
|
||||||
bd.stex.texId = texD->m_id;
|
bd.stex.d[elem].texId = texD->m_id;
|
||||||
bd.stex.texGeneration = texD->generation;
|
bd.stex.d[elem].texGeneration = texD->generation;
|
||||||
bd.stex.samplerId = samplerD->m_id;
|
bd.stex.d[elem].samplerId = samplerD->m_id;
|
||||||
bd.stex.samplerGeneration = samplerD->generation;
|
bd.stex.d[elem].samplerGeneration = samplerD->generation;
|
||||||
}
|
}
|
||||||
texD->lastActiveFrameSlot = currentFrameSlot;
|
texD->lastActiveFrameSlot = currentFrameSlot;
|
||||||
samplerD->lastActiveFrameSlot = currentFrameSlot;
|
samplerD->lastActiveFrameSlot = currentFrameSlot;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::ImageLoad:
|
case QRhiShaderResourceBinding::ImageLoad:
|
||||||
case QRhiShaderResourceBinding::ImageStore:
|
case QRhiShaderResourceBinding::ImageStore:
|
||||||
@ -2981,12 +2991,16 @@ bool QMetalShaderResourceBindings::build()
|
|||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
case QRhiShaderResourceBinding::SampledTexture:
|
||||||
{
|
{
|
||||||
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
|
const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
|
||||||
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
|
bd.stex.count = data->count;
|
||||||
bd.stex.texId = texD->m_id;
|
for (int elem = 0; elem < data->count; ++elem) {
|
||||||
bd.stex.texGeneration = texD->generation;
|
QMetalTexture *texD = QRHI_RES(QMetalTexture, data->texSamplers[elem].tex);
|
||||||
bd.stex.samplerId = samplerD->m_id;
|
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler);
|
||||||
bd.stex.samplerGeneration = samplerD->generation;
|
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;
|
break;
|
||||||
case QRhiShaderResourceBinding::ImageLoad:
|
case QRhiShaderResourceBinding::ImageLoad:
|
||||||
@ -3241,8 +3255,12 @@ static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c)
|
|||||||
id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
|
id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
|
||||||
QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
|
QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
|
||||||
{
|
{
|
||||||
QShaderKey key = { QShader::MetalLibShader, 12, shaderVariant };
|
QShaderKey key = { QShader::MetalLibShader, 20, shaderVariant };
|
||||||
QShaderCode mtllib = shader.shader(key);
|
QShaderCode mtllib = shader.shader(key);
|
||||||
|
if (mtllib.shader().isEmpty()) {
|
||||||
|
key.setSourceVersion(12);
|
||||||
|
mtllib = shader.shader(key);
|
||||||
|
}
|
||||||
if (!mtllib.shader().isEmpty()) {
|
if (!mtllib.shader().isEmpty()) {
|
||||||
dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
|
dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
|
||||||
size_t(mtllib.shader().size()),
|
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);
|
QShaderCode mslSource = shader.shader(key);
|
||||||
if (mslSource.shader().isEmpty()) {
|
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;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
|
NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
|
||||||
MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
|
MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
|
||||||
opts.languageVersion = MTLLanguageVersion1_2;
|
opts.languageVersion = key.sourceVersion() == 20 ? MTLLanguageVersion2_0 : MTLLanguageVersion1_2;
|
||||||
NSError *err = nil;
|
NSError *err = nil;
|
||||||
id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
|
id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
|
||||||
[opts release];
|
[opts release];
|
||||||
|
@ -197,10 +197,13 @@ struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings
|
|||||||
uint generation;
|
uint generation;
|
||||||
};
|
};
|
||||||
struct BoundSampledTextureData {
|
struct BoundSampledTextureData {
|
||||||
|
int count;
|
||||||
|
struct {
|
||||||
quint64 texId;
|
quint64 texId;
|
||||||
uint texGeneration;
|
uint texGeneration;
|
||||||
quint64 samplerId;
|
quint64 samplerId;
|
||||||
uint samplerGeneration;
|
uint samplerGeneration;
|
||||||
|
} d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
|
||||||
};
|
};
|
||||||
struct BoundStorageImageData {
|
struct BoundStorageImageData {
|
||||||
quint64 id;
|
quint64 id;
|
||||||
|
@ -2487,7 +2487,8 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
|
|||||||
QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
|
QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
|
||||||
|
|
||||||
QVarLengthArray<VkDescriptorBufferInfo, 8> bufferInfos;
|
QVarLengthArray<VkDescriptorBufferInfo, 8> bufferInfos;
|
||||||
QVarLengthArray<VkDescriptorImageInfo, 8> imageInfos;
|
using ArrayOfImageDesc = QVarLengthArray<VkDescriptorImageInfo, 8>;
|
||||||
|
QVarLengthArray<ArrayOfImageDesc, 8> imageInfos;
|
||||||
QVarLengthArray<VkWriteDescriptorSet, 12> writeInfos;
|
QVarLengthArray<VkWriteDescriptorSet, 12> writeInfos;
|
||||||
QVarLengthArray<QPair<int, int>, 12> infoIndices;
|
QVarLengthArray<QPair<int, int>, 12> infoIndices;
|
||||||
|
|
||||||
@ -2530,17 +2531,22 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
|
|||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
case QRhiShaderResourceBinding::SampledTexture:
|
||||||
{
|
{
|
||||||
QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex);
|
const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
|
||||||
QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler);
|
writeInfo.descriptorCount = data->count; // arrays of combined image samplers are supported
|
||||||
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
bd.stex.texId = texD->m_id;
|
ArrayOfImageDesc imageInfo(data->count);
|
||||||
bd.stex.texGeneration = texD->generation;
|
for (int elem = 0; elem < data->count; ++elem) {
|
||||||
bd.stex.samplerId = samplerD->m_id;
|
QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex);
|
||||||
bd.stex.samplerGeneration = samplerD->generation;
|
QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler);
|
||||||
VkDescriptorImageInfo imageInfo;
|
bd.stex.d[elem].texId = texD->m_id;
|
||||||
imageInfo.sampler = samplerD->sampler;
|
bd.stex.d[elem].texGeneration = texD->generation;
|
||||||
imageInfo.imageView = texD->imageView;
|
bd.stex.d[elem].samplerId = samplerD->m_id;
|
||||||
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
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();
|
imageInfoIndex = imageInfos.count();
|
||||||
imageInfos.append(imageInfo);
|
imageInfos.append(imageInfo);
|
||||||
}
|
}
|
||||||
@ -2555,10 +2561,10 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
|
|||||||
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||||
bd.simage.id = texD->m_id;
|
bd.simage.id = texD->m_id;
|
||||||
bd.simage.generation = texD->generation;
|
bd.simage.generation = texD->generation;
|
||||||
VkDescriptorImageInfo imageInfo;
|
ArrayOfImageDesc imageInfo(1);
|
||||||
imageInfo.sampler = VK_NULL_HANDLE;
|
imageInfo[0].sampler = VK_NULL_HANDLE;
|
||||||
imageInfo.imageView = view;
|
imageInfo[0].imageView = view;
|
||||||
imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
imageInfoIndex = imageInfos.count();
|
imageInfoIndex = imageInfos.count();
|
||||||
imageInfos.append(imageInfo);
|
imageInfos.append(imageInfo);
|
||||||
}
|
}
|
||||||
@ -2596,7 +2602,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
|
|||||||
if (bufferInfoIndex >= 0)
|
if (bufferInfoIndex >= 0)
|
||||||
writeInfos[i].pBufferInfo = &bufferInfos[bufferInfoIndex];
|
writeInfos[i].pBufferInfo = &bufferInfos[bufferInfoIndex];
|
||||||
else if (imageInfoIndex >= 0)
|
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);
|
df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.count()), writeInfos.constData(), 0, nullptr);
|
||||||
@ -4210,24 +4216,30 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
|
|||||||
break;
|
break;
|
||||||
case QRhiShaderResourceBinding::SampledTexture:
|
case QRhiShaderResourceBinding::SampledTexture:
|
||||||
{
|
{
|
||||||
QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex);
|
const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
|
||||||
QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler);
|
if (bd.stex.count != data->count) {
|
||||||
|
bd.stex.count = data->count;
|
||||||
|
rewriteDescSet = true;
|
||||||
|
}
|
||||||
|
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;
|
texD->lastActiveFrameSlot = currentFrameSlot;
|
||||||
samplerD->lastActiveFrameSlot = currentFrameSlot;
|
samplerD->lastActiveFrameSlot = currentFrameSlot;
|
||||||
trackedRegisterTexture(&passResTracker, texD,
|
trackedRegisterTexture(&passResTracker, texD,
|
||||||
QRhiPassResourceTracker::TexSample,
|
QRhiPassResourceTracker::TexSample,
|
||||||
QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
|
QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
|
||||||
|
if (texD->generation != bd.stex.d[elem].texGeneration
|
||||||
if (texD->generation != bd.stex.texGeneration
|
|| texD->m_id != bd.stex.d[elem].texId
|
||||||
|| texD->m_id != bd.stex.texId
|
|| samplerD->generation != bd.stex.d[elem].samplerGeneration
|
||||||
|| samplerD->generation != bd.stex.samplerGeneration
|
|| samplerD->m_id != bd.stex.d[elem].samplerId)
|
||||||
|| samplerD->m_id != bd.stex.samplerId)
|
|
||||||
{
|
{
|
||||||
rewriteDescSet = true;
|
rewriteDescSet = true;
|
||||||
bd.stex.texId = texD->m_id;
|
bd.stex.d[elem].texId = texD->m_id;
|
||||||
bd.stex.texGeneration = texD->generation;
|
bd.stex.d[elem].texGeneration = texD->generation;
|
||||||
bd.stex.samplerId = samplerD->m_id;
|
bd.stex.d[elem].samplerId = samplerD->m_id;
|
||||||
bd.stex.samplerGeneration = samplerD->generation;
|
bd.stex.d[elem].samplerGeneration = samplerD->generation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -6065,7 +6077,10 @@ bool QVkShaderResourceBindings::build()
|
|||||||
memset(&vkbinding, 0, sizeof(vkbinding));
|
memset(&vkbinding, 0, sizeof(vkbinding));
|
||||||
vkbinding.binding = uint32_t(b->binding);
|
vkbinding.binding = uint32_t(b->binding);
|
||||||
vkbinding.descriptorType = toVkDescriptorType(b);
|
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);
|
vkbinding.stageFlags = toVkShaderStageFlags(b->stage);
|
||||||
vkbindings.append(vkbinding);
|
vkbindings.append(vkbinding);
|
||||||
}
|
}
|
||||||
|
@ -254,10 +254,13 @@ struct QVkShaderResourceBindings : public QRhiShaderResourceBindings
|
|||||||
uint generation;
|
uint generation;
|
||||||
};
|
};
|
||||||
struct BoundSampledTextureData {
|
struct BoundSampledTextureData {
|
||||||
|
int count;
|
||||||
|
struct {
|
||||||
quint64 texId;
|
quint64 texId;
|
||||||
uint texGeneration;
|
uint texGeneration;
|
||||||
quint64 samplerId;
|
quint64 samplerId;
|
||||||
uint samplerGeneration;
|
uint samplerGeneration;
|
||||||
|
} d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
|
||||||
};
|
};
|
||||||
struct BoundStorageImageData {
|
struct BoundStorageImageData {
|
||||||
quint64 id;
|
quint64 id;
|
||||||
|
@ -44,5 +44,6 @@ qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simple.vert.qsb simple.vert
|
|||||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simple.frag.qsb simple.frag
|
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simple.frag.qsb simple.frag
|
||||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.vert.qsb simpletextured.vert
|
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.vert.qsb simpletextured.vert
|
||||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.frag.qsb simpletextured.frag
|
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.frag.qsb simpletextured.frag
|
||||||
|
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 20 -o simpletextured_array.frag.qsb simpletextured_array.frag
|
||||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.vert.qsb textured.vert
|
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.vert.qsb textured.vert
|
||||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.frag.qsb textured.frag
|
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.frag.qsb textured.frag
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
17
tests/auto/gui/rhi/qrhi/data/simpletextured_array.frag
Normal file
17
tests/auto/gui/rhi/qrhi/data/simpletextured_array.frag
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 uv;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(binding = 0) uniform sampler2D tex[3];
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 c0 = texture(tex[0], uv);
|
||||||
|
vec4 c1 = texture(tex[1], uv);
|
||||||
|
vec4 c2 = texture(tex[2], uv);
|
||||||
|
vec4 cc = c0 + c1 + c2;
|
||||||
|
vec4 c = vec4(clamp(cc.r, 0.0, 1.0), clamp(cc.g, 0.0, 1.0), clamp(cc.b, 0.0, 1.0), clamp(cc.a, 0.0, 1.0));
|
||||||
|
c.rgb *= c.a;
|
||||||
|
fragColor = c;
|
||||||
|
}
|
BIN
tests/auto/gui/rhi/qrhi/data/simpletextured_array.frag.qsb
Normal file
BIN
tests/auto/gui/rhi/qrhi/data/simpletextured_array.frag.qsb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -91,6 +91,8 @@ private slots:
|
|||||||
void renderToTextureSimple();
|
void renderToTextureSimple();
|
||||||
void renderToTextureTexturedQuad_data();
|
void renderToTextureTexturedQuad_data();
|
||||||
void renderToTextureTexturedQuad();
|
void renderToTextureTexturedQuad();
|
||||||
|
void renderToTextureArrayOfTexturedQuad_data();
|
||||||
|
void renderToTextureArrayOfTexturedQuad();
|
||||||
void renderToTextureTexturedQuadAndUniformBuffer_data();
|
void renderToTextureTexturedQuadAndUniformBuffer_data();
|
||||||
void renderToTextureTexturedQuadAndUniformBuffer();
|
void renderToTextureTexturedQuadAndUniformBuffer();
|
||||||
void renderToWindowSimple_data();
|
void renderToWindowSimple_data();
|
||||||
@ -1468,6 +1470,147 @@ void tst_QRhi::renderToTextureTexturedQuad()
|
|||||||
QVERIFY(qGreen(result.pixel(214, 191)) > 2 * qBlue(result.pixel(214, 191)));
|
QVERIFY(qGreen(result.pixel(214, 191)) > 2 * qBlue(result.pixel(214, 191)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QRhi::renderToTextureArrayOfTexturedQuad_data()
|
||||||
|
{
|
||||||
|
rhiTestData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QRhi::renderToTextureArrayOfTexturedQuad()
|
||||||
|
{
|
||||||
|
QFETCH(QRhi::Implementation, impl);
|
||||||
|
QFETCH(QRhiInitParams *, initParams);
|
||||||
|
|
||||||
|
QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
|
||||||
|
if (!rhi)
|
||||||
|
QSKIP("QRhi could not be created, skipping testing rendering");
|
||||||
|
|
||||||
|
QImage inputImage;
|
||||||
|
inputImage.load(QLatin1String(":/data/qt256.png"));
|
||||||
|
QVERIFY(!inputImage.isNull());
|
||||||
|
|
||||||
|
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size(), 1,
|
||||||
|
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
|
||||||
|
QVERIFY(texture->build());
|
||||||
|
|
||||||
|
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ texture.data() }));
|
||||||
|
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
|
||||||
|
rt->setRenderPassDescriptor(rpDesc.data());
|
||||||
|
QVERIFY(rt->build());
|
||||||
|
|
||||||
|
QRhiCommandBuffer *cb = nullptr;
|
||||||
|
QVERIFY(rhi->beginOffscreenFrame(&cb) == QRhi::FrameOpSuccess);
|
||||||
|
QVERIFY(cb);
|
||||||
|
|
||||||
|
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
|
||||||
|
|
||||||
|
static const float verticesUvs[] = {
|
||||||
|
-1.0f, -1.0f, 0.0f, 0.0f,
|
||||||
|
1.0f, -1.0f, 1.0f, 0.0f,
|
||||||
|
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||||
|
1.0f, 1.0f, 1.0f, 1.0f
|
||||||
|
};
|
||||||
|
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(verticesUvs)));
|
||||||
|
QVERIFY(vbuf->build());
|
||||||
|
updates->uploadStaticBuffer(vbuf.data(), verticesUvs);
|
||||||
|
|
||||||
|
// In this test we pass 3 textures (and samplers) to the fragment shader in
|
||||||
|
// form of an array of combined image samplers.
|
||||||
|
|
||||||
|
QScopedPointer<QRhiTexture> inputTexture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size()));
|
||||||
|
QVERIFY(inputTexture->build());
|
||||||
|
updates->uploadTexture(inputTexture.data(), inputImage);
|
||||||
|
|
||||||
|
QImage redImage(inputImage.size(), QImage::Format_RGBA8888);
|
||||||
|
redImage.fill(Qt::red);
|
||||||
|
|
||||||
|
QScopedPointer<QRhiTexture> redTexture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size()));
|
||||||
|
QVERIFY(redTexture->build());
|
||||||
|
updates->uploadTexture(redTexture.data(), redImage);
|
||||||
|
|
||||||
|
QImage greenImage(inputImage.size(), QImage::Format_RGBA8888);
|
||||||
|
greenImage.fill(Qt::green);
|
||||||
|
|
||||||
|
QScopedPointer<QRhiTexture> greenTexture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size()));
|
||||||
|
QVERIFY(greenTexture->build());
|
||||||
|
updates->uploadTexture(greenTexture.data(), greenImage);
|
||||||
|
|
||||||
|
QScopedPointer<QRhiSampler> sampler(rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
|
||||||
|
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
|
||||||
|
QVERIFY(sampler->build());
|
||||||
|
|
||||||
|
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
|
||||||
|
QRhiShaderResourceBinding::TextureAndSampler texSamplers[3] = {
|
||||||
|
{ inputTexture.data(), sampler.data() },
|
||||||
|
{ redTexture.data(), sampler.data() },
|
||||||
|
{ greenTexture.data(), sampler.data() }
|
||||||
|
};
|
||||||
|
srb->setBindings({
|
||||||
|
QRhiShaderResourceBinding::sampledTextures(0, QRhiShaderResourceBinding::FragmentStage, 3, texSamplers)
|
||||||
|
});
|
||||||
|
QVERIFY(srb->build());
|
||||||
|
|
||||||
|
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
|
||||||
|
pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
|
||||||
|
QShader vs = loadShader(":/data/simpletextured.vert.qsb");
|
||||||
|
QVERIFY(vs.isValid());
|
||||||
|
QShader fs = loadShader(":/data/simpletextured_array.frag.qsb");
|
||||||
|
QVERIFY(fs.isValid());
|
||||||
|
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
|
||||||
|
QRhiVertexInputLayout inputLayout;
|
||||||
|
inputLayout.setBindings({ { 4 * sizeof(float) } });
|
||||||
|
inputLayout.setAttributes({
|
||||||
|
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
|
||||||
|
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
|
||||||
|
});
|
||||||
|
pipeline->setVertexInputLayout(inputLayout);
|
||||||
|
pipeline->setShaderResourceBindings(srb.data());
|
||||||
|
pipeline->setRenderPassDescriptor(rpDesc.data());
|
||||||
|
|
||||||
|
QVERIFY(pipeline->build());
|
||||||
|
|
||||||
|
cb->beginPass(rt.data(), Qt::black, { 1.0f, 0 }, updates);
|
||||||
|
cb->setGraphicsPipeline(pipeline.data());
|
||||||
|
cb->setShaderResources();
|
||||||
|
cb->setViewport({ 0, 0, float(texture->pixelSize().width()), float(texture->pixelSize().height()) });
|
||||||
|
QRhiCommandBuffer::VertexInput vbindings(vbuf.data(), 0);
|
||||||
|
cb->setVertexInput(0, 1, &vbindings);
|
||||||
|
cb->draw(4);
|
||||||
|
|
||||||
|
QRhiReadbackResult readResult;
|
||||||
|
QImage result;
|
||||||
|
readResult.completed = [&readResult, &result] {
|
||||||
|
result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
|
||||||
|
readResult.pixelSize.width(), readResult.pixelSize.height(),
|
||||||
|
QImage::Format_RGBA8888_Premultiplied);
|
||||||
|
};
|
||||||
|
QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
|
||||||
|
readbackBatch->readBackTexture({ texture.data() }, &readResult);
|
||||||
|
cb->endPass(readbackBatch);
|
||||||
|
|
||||||
|
rhi->endOffscreenFrame();
|
||||||
|
|
||||||
|
QVERIFY(!result.isNull());
|
||||||
|
|
||||||
|
if (impl == QRhi::Null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Flip with D3D and Metal because these have Y down in images. Vulkan does
|
||||||
|
// not need this because there Y is down both in images and in NDC, which
|
||||||
|
// just happens to give correct results with our OpenGL-targeted vertex and
|
||||||
|
// UV data.
|
||||||
|
if (rhi->isYUpInFramebuffer() != rhi->isYUpInNDC())
|
||||||
|
result = std::move(result).mirrored();
|
||||||
|
|
||||||
|
// we added the input image + red + green together, so red and green must be all 1
|
||||||
|
for (int y = 0; y < result.height(); ++y) {
|
||||||
|
for (int x = 0; x < result.width(); ++x) {
|
||||||
|
const QRgb pixel = result.pixel(x, y);
|
||||||
|
QCOMPARE(qRed(pixel), 255);
|
||||||
|
QCOMPARE(qGreen(pixel), 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QRhi::renderToTextureTexturedQuadAndUniformBuffer_data()
|
void tst_QRhi::renderToTextureTexturedQuadAndUniformBuffer_data()
|
||||||
{
|
{
|
||||||
rhiTestData();
|
rhiTestData();
|
||||||
|
Loading…
Reference in New Issue
Block a user