rhi: gl: Add a feature flag for reading back non-zero mip levels
The joys of "level - Specifies the mipmap level of the texture image to be attached, which must be 0." for glFramebufferTexture2D in OpenGL ES 2.0. Change-Id: Iaf19502f48d7ba73b26abb72535bfa6696a1e182 Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
parent
32924110ce
commit
9baf69c765
@ -572,7 +572,13 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
||||
supported for QRhiBuffer instances with a usage different than
|
||||
UniformBuffer. While this is supported in the majority of cases, it will be
|
||||
unsupported with OpenGL ES older than 3.0.
|
||||
*/
|
||||
|
||||
\value ReadBackNonBaseMipLevel Indicates that specifying a mip level other
|
||||
than 0 is supported when reading back texture contents. When not supported,
|
||||
specifying a non-zero level in QRhiReadbackDescription leads to returning
|
||||
an all-zero image. In practice this feature will be unsupported with OpenGL
|
||||
ES 2.0, while it will likely be supported everywhere else.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QRhi::BeginFrameFlag
|
||||
|
@ -1436,7 +1436,8 @@ public:
|
||||
BaseVertex,
|
||||
BaseInstance,
|
||||
TriangleFanTopology,
|
||||
ReadBackNonUniformBuffer
|
||||
ReadBackNonUniformBuffer,
|
||||
ReadBackNonBaseMipLevel
|
||||
};
|
||||
|
||||
enum BeginFrameFlag {
|
||||
|
@ -473,6 +473,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
|
||||
return false;
|
||||
case QRhi::ReadBackNonUniformBuffer:
|
||||
return true;
|
||||
case QRhi::ReadBackNonBaseMipLevel:
|
||||
return true;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return false;
|
||||
|
@ -500,6 +500,11 @@ bool QRhiGles2::create(QRhi::Flags flags)
|
||||
// extension(s) (which is not in ES 3.0...messy)
|
||||
caps.properMapBuffer = f->hasOpenGLExtension(QOpenGLExtensions::MapBufferRange);
|
||||
|
||||
if (caps.gles)
|
||||
caps.nonBaseLevelFramebufferTexture = caps.ctxMajor >= 3; // ES 3.0
|
||||
else
|
||||
caps.nonBaseLevelFramebufferTexture = true;
|
||||
|
||||
if (!caps.gles) {
|
||||
f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
f->glEnable(GL_POINT_SPRITE);
|
||||
@ -744,6 +749,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
|
||||
return true;
|
||||
case QRhi::ReadBackNonUniformBuffer:
|
||||
return !caps.gles || caps.properMapBuffer;
|
||||
case QRhi::ReadBackNonBaseMipLevel:
|
||||
return caps.nonBaseLevelFramebufferTexture;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return false;
|
||||
@ -2156,23 +2163,31 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
|
||||
QRhiReadbackResult *result = cmd.args.readPixels.result;
|
||||
GLuint tex = cmd.args.readPixels.texture;
|
||||
GLuint fbo = 0;
|
||||
int mipLevel = 0;
|
||||
if (tex) {
|
||||
result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h);
|
||||
result->format = cmd.args.readPixels.format;
|
||||
f->glGenFramebuffers(1, &fbo);
|
||||
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, cmd.args.readPixels.level);
|
||||
mipLevel = cmd.args.readPixels.level;
|
||||
if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
|
||||
f->glGenFramebuffers(1, &fbo);
|
||||
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, mipLevel);
|
||||
}
|
||||
} else {
|
||||
result->pixelSize = currentSwapChain->pixelSize;
|
||||
result->format = QRhiTexture::RGBA8;
|
||||
// readPixels handles multisample resolving implicitly
|
||||
}
|
||||
result->data.resize(result->pixelSize.width() * result->pixelSize.height() * 4);
|
||||
// With GLES (2.0?) GL_RGBA is the only mandated readback format, so stick with it.
|
||||
f->glReadPixels(0, 0, result->pixelSize.width(), result->pixelSize.height(),
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
result->data.data());
|
||||
if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
|
||||
// With GLES (2.0?) GL_RGBA is the only mandated readback format, so stick with it.
|
||||
f->glReadPixels(0, 0, result->pixelSize.width(), result->pixelSize.height(),
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
result->data.data());
|
||||
} else {
|
||||
result->data.fill('\0');
|
||||
}
|
||||
if (fbo) {
|
||||
f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
|
||||
f->glDeleteFramebuffers(1, &fbo);
|
||||
|
@ -753,7 +753,8 @@ public:
|
||||
baseVertex(false),
|
||||
compute(false),
|
||||
textureCompareMode(false),
|
||||
properMapBuffer(false)
|
||||
properMapBuffer(false),
|
||||
nonBaseLevelFramebufferTexture(false)
|
||||
{ }
|
||||
int ctxMajor;
|
||||
int ctxMinor;
|
||||
@ -785,6 +786,7 @@ public:
|
||||
uint compute : 1;
|
||||
uint textureCompareMode : 1;
|
||||
uint properMapBuffer : 1;
|
||||
uint nonBaseLevelFramebufferTexture : 1;
|
||||
} caps;
|
||||
QGles2SwapChain *currentSwapChain = nullptr;
|
||||
QVector<GLint> supportedCompressedFormats;
|
||||
|
@ -559,6 +559,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
|
||||
return false;
|
||||
case QRhi::ReadBackNonUniformBuffer:
|
||||
return true;
|
||||
case QRhi::ReadBackNonBaseMipLevel:
|
||||
return true;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return false;
|
||||
|
@ -3823,6 +3823,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
|
||||
return true;
|
||||
case QRhi::ReadBackNonUniformBuffer:
|
||||
return true;
|
||||
case QRhi::ReadBackNonBaseMipLevel:
|
||||
return true;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return false;
|
||||
|
@ -277,7 +277,8 @@ void tst_QRhi::create()
|
||||
QRhi::BaseVertex,
|
||||
QRhi::BaseInstance,
|
||||
QRhi::TriangleFanTopology,
|
||||
QRhi::ReadBackNonUniformBuffer
|
||||
QRhi::ReadBackNonUniformBuffer,
|
||||
QRhi::ReadBackNonBaseMipLevel
|
||||
};
|
||||
for (size_t i = 0; i <sizeof(features) / sizeof(QRhi::Feature); ++i)
|
||||
rhi->isFeatureSupported(features[i]);
|
||||
@ -965,10 +966,16 @@ void tst_QRhi::resourceUpdateBatchRGBATextureMip()
|
||||
QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
|
||||
readResult.pixelSize.width(), readResult.pixelSize.height(),
|
||||
red.format());
|
||||
|
||||
// Compare to a scaled version; we can do this safely only because we
|
||||
// only have plain red pixels in the source image.
|
||||
QImage expectedImage = red.scaled(expectedSize);
|
||||
QImage expectedImage;
|
||||
if (level == 0 || rhi->isFeatureSupported(QRhi::ReadBackNonBaseMipLevel)) {
|
||||
// Compare to a scaled version; we can do this safely only because we
|
||||
// only have plain red pixels in the source image.
|
||||
expectedImage = red.scaled(expectedSize);
|
||||
} else {
|
||||
qDebug("Expecting all-zero image for level %d because reading back a level other than 0 is not supported", level);
|
||||
expectedImage = QImage(readResult.pixelSize, red.format());
|
||||
expectedImage.fill(0);
|
||||
}
|
||||
QVERIFY(imageRGBAEquals(expectedImage, wrapperImage));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user