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
|
||||
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,11 +3125,15 @@ 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:
|
||||
Q_FALLTHROUGH();
|
||||
@ -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("
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
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.texId = texD->m_id;
|
||||
bd.stex.texGeneration = texD->generation;
|
||||
bd.stex.samplerId = samplerD->m_id;
|
||||
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;
|
||||
@ -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;
|
||||
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)) {
|
||||
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 });
|
||||
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 (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 (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;
|
||||
|
@ -210,10 +210,13 @@ struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings
|
||||
uint generation;
|
||||
};
|
||||
struct BoundSampledTextureData {
|
||||
int count;
|
||||
struct {
|
||||
quint64 texId;
|
||||
uint texGeneration;
|
||||
quint64 samplerId;
|
||||
uint samplerGeneration;
|
||||
} d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
|
||||
};
|
||||
struct BoundStorageImageData {
|
||||
quint64 id;
|
||||
|
@ -919,10 +919,12 @@ void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
|
||||
hasDynamicOffsetInSrb = true;
|
||||
break;
|
||||
case QRhiShaderResourceBinding::SampledTexture:
|
||||
for (int elem = 0; elem < b->u.stex.count; ++elem) {
|
||||
trackedRegisterTexture(&passResTracker,
|
||||
QRHI_RES(QGles2Texture, b->u.stex.tex),
|
||||
QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex),
|
||||
QRhiPassResourceTracker::TexSample,
|
||||
QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
|
||||
}
|
||||
break;
|
||||
case QRhiShaderResourceBinding::ImageLoad:
|
||||
case QRhiShaderResourceBinding::ImageStore:
|
||||
@ -2574,11 +2576,11 @@ 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 (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));
|
||||
@ -2602,11 +2604,12 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
|
||||
texD->samplerState = samplerD->d;
|
||||
}
|
||||
|
||||
f->glUniform1i(sampler.glslLocation, texUnit);
|
||||
f->glUniform1i(sampler.glslLocation + elem, texUnit);
|
||||
++texUnit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QRhiShaderResourceBinding::ImageLoad:
|
||||
case QRhiShaderResourceBinding::ImageStore:
|
||||
|
@ -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);
|
||||
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, texD->d->tex });
|
||||
res[VERTEX].samplers.append({ nativeBindingSampler, samplerD->d->samplerState });
|
||||
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 });
|
||||
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 });
|
||||
res[COMPUTE].textures.append({ nativeBindingTexture + elem, texD->d->tex });
|
||||
res[COMPUTE].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1020,22 +1023,29 @@ 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;
|
||||
}
|
||||
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.texId = texD->m_id;
|
||||
bd.stex.texGeneration = texD->generation;
|
||||
bd.stex.samplerId = samplerD->m_id;
|
||||
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;
|
||||
}
|
||||
texD->lastActiveFrameSlot = currentFrameSlot;
|
||||
samplerD->lastActiveFrameSlot = currentFrameSlot;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QRhiShaderResourceBinding::ImageLoad:
|
||||
case QRhiShaderResourceBinding::ImageStore:
|
||||
@ -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];
|
||||
|
@ -197,10 +197,13 @@ struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings
|
||||
uint generation;
|
||||
};
|
||||
struct BoundSampledTextureData {
|
||||
int count;
|
||||
struct {
|
||||
quint64 texId;
|
||||
uint texGeneration;
|
||||
quint64 samplerId;
|
||||
uint samplerGeneration;
|
||||
} d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
|
||||
};
|
||||
struct BoundStorageImageData {
|
||||
quint64 id;
|
||||
|
@ -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);
|
||||
const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
|
||||
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;
|
||||
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)
|
||||
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.texId = texD->m_id;
|
||||
bd.stex.texGeneration = texD->generation;
|
||||
bd.stex.samplerId = samplerD->m_id;
|
||||
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;
|
||||
@ -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);
|
||||
}
|
||||
|
@ -254,10 +254,13 @@ struct QVkShaderResourceBindings : public QRhiShaderResourceBindings
|
||||
uint generation;
|
||||
};
|
||||
struct BoundSampledTextureData {
|
||||
int count;
|
||||
struct {
|
||||
quint64 texId;
|
||||
uint texGeneration;
|
||||
quint64 samplerId;
|
||||
uint samplerGeneration;
|
||||
} d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
|
||||
};
|
||||
struct BoundStorageImageData {
|
||||
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 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 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.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 renderToTextureTexturedQuad_data();
|
||||
void renderToTextureTexturedQuad();
|
||||
void renderToTextureArrayOfTexturedQuad_data();
|
||||
void renderToTextureArrayOfTexturedQuad();
|
||||
void renderToTextureTexturedQuadAndUniformBuffer_data();
|
||||
void renderToTextureTexturedQuadAndUniformBuffer();
|
||||
void renderToWindowSimple_data();
|
||||
@ -1468,6 +1470,147 @@ void tst_QRhi::renderToTextureTexturedQuad()
|
||||
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()
|
||||
{
|
||||
rhiTestData();
|
||||
|
Loading…
Reference in New Issue
Block a user