rhi: gl: Adjust texture and renderbuffer sizes automatically

Enforce the maximum texture size limit both for textures and renderbuffers,
showing warnings and adjusting the size automatically.

On the low end ES 2.0 devices we can easily hit a maximum size of 2048x2048.
For example a Raspberry Pi running in full HD with the Controls 2 Material
style breaks right away as it tries to create slightly wider than 2048 texture
for its ShaderEffect.

Instead of breaking with an obscure framebuffer incomplete warning at best,
show something more informative and adjust the sizes so that something shows
up on the screen still.

(naturally applications not taking the maximum sizes returned from QRhi
into account are technically incorrect and may well end up with incorrect
rendering, but we should still try handling this as gracefully as possible)

Change-Id: Ib75eb893ab4774e1a3c49ed2d14c6bf9be8c807a
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Laszlo Agocs 2019-06-27 15:23:20 +02:00
parent 13a7777bbf
commit 03ebad7bfd
2 changed files with 47 additions and 18 deletions

View File

@ -497,6 +497,43 @@ int QRhiGles2::effectiveSampleCount(int sampleCount) const
return s;
}
static inline bool isPowerOfTwo(int x)
{
// Assumption: x >= 1
return x == (x & -x);
}
QSize QRhiGles2::safeTextureSize(const QSize &pixelSize) const
{
QSize size = pixelSize.isEmpty() ? QSize(1, 1) : pixelSize;
if (!caps.npotTexture) {
if (!isPowerOfTwo(size.width())) {
qWarning("Texture width %d is not a power of two, adjusting",
size.width());
size.setWidth(qNextPowerOfTwo(size.width()));
}
if (!isPowerOfTwo(size.height())) {
qWarning("Texture height %d is not a power of two, adjusting",
size.height());
size.setHeight(qNextPowerOfTwo(size.height()));
}
}
if (size.width() > caps.maxTextureSize) {
qWarning("Texture width %d exceeds maximum width %d, adjusting",
size.width(), caps.maxTextureSize);
size.setWidth(caps.maxTextureSize);
}
if (size.height() > caps.maxTextureSize) {
qWarning("Texture height %d exceeds maximum height %d, adjusting",
size.height(), caps.maxTextureSize);
size.setHeight(caps.maxTextureSize);
}
return size;
}
QRhiSwapChain *QRhiGles2::createSwapChain()
{
return new QGles2SwapChain(this);
@ -2276,26 +2313,25 @@ bool QGles2RenderBuffer::build()
qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color");
}
if (m_pixelSize.isEmpty())
return false;
if (!rhiD->ensureContext())
return false;
rhiD->f->glGenRenderbuffers(1, &renderbuffer);
rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
const QSize size = rhiD->safeTextureSize(m_pixelSize);
switch (m_type) {
case QRhiRenderBuffer::DepthStencil:
if (rhiD->caps.msaaRenderBuffer && samples > 1) {
const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8;
rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage,
m_pixelSize.width(), m_pixelSize.height());
size.width(), size.height());
stencilRenderbuffer = 0;
} else if (rhiD->caps.packedDepthStencil || rhiD->caps.needsDepthStencilCombinedAttach) {
const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8;
rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, storage,
m_pixelSize.width(), m_pixelSize.height());
size.width(), size.height());
stencilRenderbuffer = 0;
} else {
GLenum depthStorage = GL_DEPTH_COMPONENT;
@ -2307,21 +2343,21 @@ bool QGles2RenderBuffer::build()
}
const GLenum stencilStorage = rhiD->caps.gles ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX;
rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, depthStorage,
m_pixelSize.width(), m_pixelSize.height());
size.width(), size.height());
rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer);
rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer);
rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, stencilStorage,
m_pixelSize.width(), m_pixelSize.height());
size.width(), size.height());
}
QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
break;
case QRhiRenderBuffer::Color:
if (rhiD->caps.msaaRenderBuffer && samples > 1)
rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8,
m_pixelSize.width(), m_pixelSize.height());
size.width(), size.height());
else
rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA4,
m_pixelSize.width(), m_pixelSize.height());
size.width(), size.height());
QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
break;
default:
@ -2371,12 +2407,6 @@ void QGles2Texture::release()
rhiD->unregisterResource(this);
}
static inline bool isPowerOfTwo(int x)
{
// Assumption: x >= 1
return x == (x & -x);
}
bool QGles2Texture::prepareBuild(QSize *adjustedSize)
{
if (texture)
@ -2386,9 +2416,7 @@ bool QGles2Texture::prepareBuild(QSize *adjustedSize)
if (!rhiD->ensureContext())
return false;
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
if (!rhiD->caps.npotTexture && (!isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height())))
size = QSize(qNextPowerOfTwo(size.width()), qNextPowerOfTwo(size.height()));
const QSize size = rhiD->safeTextureSize(m_pixelSize);
const bool isCube = m_flags.testFlag(CubeMap);
const bool hasMipMaps = m_flags.testFlag(MipMapped);

View File

@ -611,6 +611,7 @@ public:
QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
int effectiveSampleCount(int sampleCount) const;
QSize safeTextureSize(const QSize &size) const;
QOpenGLContext *ctx = nullptr;
bool importedContext = false;