Skip to content

Commit

Permalink
Enable MSAA via render buffers
Browse files Browse the repository at this point in the history
This is the canonical Qt Quick way of enabling MSAA and fixes "Framebuffer incomplete"
errors when using the OpenGL RHI backend

Now the user can enable MSAA by using QSurfaceFormat:

QSurfaceFormat f;
f.setSamples(4);
QSurfaceFormat::setDefaultFormat(f);

MSAA is switched off by setting the sample number to 1. If the platform
doesn't support MSAA, the internal SMAA postprocessing code path can still
be used (but this code path comes with a slightly performance penalty);

postprocessingMode: RiveQtQuickItem.SMAA // in QML
  • Loading branch information
BertholdKrevert committed Mar 11, 2024
1 parent e978abe commit c7ca607
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 13 deletions.
2 changes: 1 addition & 1 deletion examples/SimpleViewer/RiveInspectorView.qml
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Item {
currentStateMachineIndex: -1

renderQuality: RiveQtQuickItem.Medium
postprocessingMode: RiveQtQuickItem.SMAA
postprocessingMode: RiveQtQuickItem.None
fillMode: RiveQtQuickItem.PreserveAspectFit

onStateMachineStringInterfaceChanged: {
Expand Down
6 changes: 5 additions & 1 deletion examples/SimpleViewer/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@
int main(int argc, char *argv[])
{

QSurfaceFormat f;
f.setSamples(4);
QSurfaceFormat::setDefaultFormat(f);

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
// Force OpenGL
// QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
#endif

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Expand Down
2 changes: 0 additions & 2 deletions src/RiveQtQuickItem/riveqsgrendernode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#include "riveqsgrendernode.h"
#include "riveqtquickitem.h"

const int RiveQSGBaseNode::MULTISAMPLE_COUNT = 4;

QRectF RiveQSGRenderNode::rect() const
{
return m_rect;
Expand Down
92 changes: 84 additions & 8 deletions src/RiveQtQuickItem/riveqsgrhirendernode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
#include <QQuickWindow>
#include <QFile>

#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLExtraFunctions>

#include <private/qrhi_p.h>
#include <private/qrhigles2_p.h>
#include <private/qsgrendernode_p.h>

#include "riveqsgrhirendernode.h"
Expand Down Expand Up @@ -325,6 +330,23 @@ bool RiveQSGRHIRenderNode::isCurrentRenderBufferA()
return m_currentRenderSurface == &m_renderSurfaceA;
}

#ifdef OPENGL_DEBUG
void GLAPIENTRY
MessageCallback( GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar* message,
const void* userParam )
{
fprintf( stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
type, severity, message );
}
#endif


void RiveQSGRHIRenderNode::prepare()
{
if (!m_window) {
Expand All @@ -338,17 +360,31 @@ void RiveQSGRHIRenderNode::prepare()
Q_ASSERT(swapChain);
Q_ASSERT(rhi);

#ifdef OPENGL_DEBUG
if (rhi->backend() == QRhi::OpenGLES2) {
const QRhiGles2NativeHandles* native = static_cast<const QRhiGles2NativeHandles*>(rhi->nativeHandles());
const auto context = native->context;
if (context) {
const auto gl = context->functions();
const auto extra = context->extraFunctions();
gl->glEnable ( GL_DEBUG_OUTPUT );
extra->glDebugMessageCallback( MessageCallback, 0 );
}
}
#endif

QRhiCommandBuffer *commandBuffer = swapChain->currentFrameCommandBuffer();

if (!m_stencilClippingBuffer) {
m_stencilClippingBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(m_rect.width(), m_rect.height()), 1);
m_stencilClippingBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(m_rect.width(), m_rect.height()), m_sampleCount);
m_stencilClippingBuffer->create();
m_cleanupList.append(m_stencilClippingBuffer);
}

bool textureCreated = m_renderSurfaceA.create(rhi, QSize(m_rect.width(), m_rect.height()), m_stencilClippingBuffer);
m_renderSurfaceB.create(rhi, QSize(m_rect.width(), m_rect.height()), m_stencilClippingBuffer);
m_renderSurfaceIntern.create(rhi, QSize(m_rect.width(), m_rect.height()), m_stencilClippingBuffer, {});
m_sampleCount = swapChain->sampleCount();
bool textureCreated = m_renderSurfaceA.create(rhi, m_sampleCount, QSize(m_rect.width(), m_rect.height()), m_stencilClippingBuffer);
m_renderSurfaceB.create(rhi, m_sampleCount, QSize(m_rect.width(), m_rect.height()), m_stencilClippingBuffer);
m_renderSurfaceIntern.create(rhi, m_sampleCount, QSize(m_rect.width(), m_rect.height()), m_stencilClippingBuffer, {});

// only set the renderSurface to A in case we created a new texture
if (textureCreated) {
Expand Down Expand Up @@ -539,7 +575,9 @@ void RiveQSGRHIRenderNode::prepare()
artboardInstance->draw(m_renderer);

if (!m_cleanUpTextureTarget) {
QRhiColorAttachment colorAttachment(m_renderSurfaceA.texture);
QRhiColorAttachment colorAttachment(m_renderSurfaceA.buffer);
colorAttachment.setResolveTexture(m_renderSurfaceA.texture);

QRhiTextureRenderTargetDescription desc(colorAttachment);
m_cleanUpTextureTarget = rhi->newTextureRenderTarget(desc);
QRhiRenderPassDescriptor *renderPassDescriptor = m_cleanUpTextureTarget->newCompatibleRenderPassDescriptor();
Expand Down Expand Up @@ -800,6 +838,12 @@ QRhiGraphicsPipeline *RiveQSGRHIRenderNode::createBlendPipeline(QRhi *rhi, QRhiR

void RiveQSGRHIRenderNode::RenderSurface::cleanUp()
{
if (buffer) {
buffer->destroy();
buffer->deleteLater();
buffer = nullptr;
}

if (texture) {
texture->destroy();
texture->deleteLater();
Expand Down Expand Up @@ -827,17 +871,25 @@ void RiveQSGRHIRenderNode::RenderSurface::cleanUp()
}
}

bool RiveQSGRHIRenderNode::RenderSurface::create(QRhi *rhi, const QSize &surfaceSize, QRhiRenderBuffer *stencilClippingBuffer,
bool RiveQSGRHIRenderNode::RenderSurface::create(QRhi *rhi, int samples, const QSize &surfaceSize, QRhiRenderBuffer *stencilClippingBuffer,
QRhiTextureRenderTarget::Flags flags)
{
bool textureCreated = false;

if (!buffer) {
buffer = rhi->newRenderBuffer(QRhiRenderBuffer::Color, surfaceSize, samples);
buffer->create();
}

if (!texture) {
texture = rhi->newTexture(QRhiTexture::RGBA8, surfaceSize, 1, QRhiTexture::RenderTarget);
texture->create();
textureCreated = true;
}
if (!target) {
QRhiColorAttachment colorAttachment(texture);
QRhiColorAttachment colorAttachment(buffer);
colorAttachment.setResolveTexture(texture);

QRhiTextureRenderTargetDescription textureTargetDesc(colorAttachment);
textureTargetDesc.setDepthStencilBuffer(stencilClippingBuffer);
target = rhi->newTextureRenderTarget(textureTargetDesc, flags);
Expand All @@ -846,12 +898,36 @@ bool RiveQSGRHIRenderNode::RenderSurface::create(QRhi *rhi, const QSize &surface
target->create();
}
if (!blendTarget) {
QRhiColorAttachment colorAttachment(texture);
QRhiColorAttachment colorAttachment(buffer);
colorAttachment.setResolveTexture(texture);

QRhiTextureRenderTargetDescription textureTargetDesc(colorAttachment);
blendTarget = rhi->newTextureRenderTarget(textureTargetDesc);
blendDesc = blendTarget->newCompatibleRenderPassDescriptor();
blendTarget->setRenderPassDescriptor(blendDesc);
blendTarget->create();
}

#ifdef OPENGL_DEBUG
if (textureCreated && rhi->backend() == QRhi::OpenGLES2) {
const QRhiGles2NativeHandles* native = static_cast<const QRhiGles2NativeHandles*>(rhi->nativeHandles());
const auto context = native->context;
if (context) {
const auto format = context->format();
qDebug() << "QSurfaceFormat is" << format;
qDebug() << "is supported: QRhi::MultisampleTexture" << rhi->isFeatureSupported(QRhi::MultisampleTexture);
qDebug() << "is supported: QRhi::MultisampleRenderBuffer" << rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer);
const auto gl = context->functions();
GLenum err;
while((err = glGetError()) != GL_NO_ERROR)
{
qDebug() << "Got an OpenGL error:" << err;

}
}

}
#endif

return textureCreated;
}
5 changes: 4 additions & 1 deletion src/RiveQtQuickItem/riveqsgrhirendernode.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class RiveQSGRHIRenderNode : public RiveQSGRenderNode
protected:
struct RenderSurface
{
QRhiRenderBuffer *buffer { nullptr };
QRhiTexture *texture { nullptr };
QRhiRenderPassDescriptor *desc { nullptr };
QRhiRenderPassDescriptor *blendDesc { nullptr };
Expand All @@ -81,7 +82,7 @@ class RiveQSGRHIRenderNode : public RiveQSGRenderNode
bool valid() { return texture != nullptr; }

// this creates all resources needed to have a texture to draw on
bool create(QRhi *rhi, const QSize &surfaceSize, QRhiRenderBuffer *stencilClippingBuffer,
bool create(QRhi *rhi, int samples, const QSize &surfaceSize, QRhiRenderBuffer *stencilClippingBuffer,
QRhiTextureRenderTarget::Flags flags = QRhiTextureRenderTarget::PreserveColorContents);
};

Expand Down Expand Up @@ -158,6 +159,8 @@ class RiveQSGRHIRenderNode : public RiveQSGRenderNode

PostprocessingSMAA *m_postprocessing { nullptr };

int m_sampleCount { 1 };

private:
QRhiGraphicsPipeline *createBlendPipeline(QRhi *rhi, QRhiRenderPassDescriptor *renderPass, QRhiShaderResourceBindings *bindings);
QRhiGraphicsPipeline *createClipPipeline(QRhi *rhi, QRhiRenderPassDescriptor *renderPassDescriptor,
Expand Down

0 comments on commit c7ca607

Please sign in to comment.