/****************************************************************************** * QSkinny - Copyright (C) 2016 Uwe Rathmann * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ #include "QskTextureRenderer.h" #include "QskColorFilter.h" #include "QskGraphic.h" #include "QskSetup.h" #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) #include #endif static inline bool qskHasOpenGLRenderer( const QQuickWindow* window ) { if ( window == nullptr ) return false; const auto renderer = window->rendererInterface(); return renderer->graphicsApi() == QSGRendererInterface::OpenGL; } static uint qskCreateTextureOpenGL( QQuickWindow* window, const QSize& size, QskTextureRenderer::PaintHelper* helper ) { const auto ratio = window ? window->effectiveDevicePixelRatio() : 1.0; const int width = ratio * size.width(); const int height = ratio * size.height(); QOpenGLFramebufferObjectFormat format1; format1.setAttachment( QOpenGLFramebufferObject::CombinedDepthStencil ); // ### TODO: get samples from window instead format1.setSamples( QOpenGLContext::currentContext()->format().samples() ); QOpenGLFramebufferObject multisampledFbo( width, height, format1 ); QOpenGLPaintDevice pd( width, height ); pd.setPaintFlipped( true ); { QPainter painter( &pd ); painter.scale( ratio, ratio ); painter.setCompositionMode( QPainter::CompositionMode_Source ); painter.fillRect( 0, 0, width, height, Qt::transparent ); painter.setCompositionMode( QPainter::CompositionMode_SourceOver ); helper->paint( &painter, size ); #if 1 if ( format1.samples() > 0 ) { /* Multisampling in the window surface might get lost as a side effect of rendering to the FBO. weired, needs to be investigated more */ painter.setRenderHint( QPainter::Antialiasing, true ); } #endif } QOpenGLFramebufferObjectFormat format2; format2.setAttachment( QOpenGLFramebufferObject::NoAttachment ); QOpenGLFramebufferObject fbo( width, height, format2 ); const QRect fboRect( 0, 0, width, height ); QOpenGLFramebufferObject::blitFramebuffer( &fbo, fboRect, &multisampledFbo, fboRect ); return fbo.takeTexture(); } static uint qskCreateTextureRaster( QQuickWindow* window, const QSize& size, QskTextureRenderer::PaintHelper* helper ) { const auto ratio = window ? window->effectiveDevicePixelRatio() : 1.0; QImage image( size * ratio, QImage::Format_RGBA8888_Premultiplied ); image.fill( Qt::transparent ); { QPainter painter( &image ); /* setting a devicePixelRatio for the image only works for value >= 1.0. So we have to scale manually. */ painter.scale( ratio, ratio ); helper->paint( &painter, size ); } const auto target = QOpenGLTexture::Target2D; auto context = QOpenGLContext::currentContext(); if ( context == nullptr ) return 0; auto& f = *context->functions(); GLint oldTexture; // we can't rely on having OpenGL Direct State Access f.glGetIntegerv( QOpenGLTexture::BindingTarget2D, &oldTexture ); GLuint textureId; f.glGenTextures( 1, &textureId ); f.glBindTexture( target, textureId ); f.glTexParameteri( target, GL_TEXTURE_MIN_FILTER, QOpenGLTexture::Nearest ); f.glTexParameteri( target, GL_TEXTURE_MAG_FILTER, QOpenGLTexture::Nearest ); f.glTexParameteri( target, GL_TEXTURE_WRAP_S, QOpenGLTexture::ClampToEdge ); f.glTexParameteri( target, GL_TEXTURE_WRAP_T, QOpenGLTexture::ClampToEdge ); if ( QOpenGLTexture::hasFeature( QOpenGLTexture::ImmutableStorage ) ) { auto& ef = *context->extraFunctions(); ef.glTexStorage2D( target, 1, QOpenGLTexture::RGBA8_UNorm, image.width(), image.height() ); f.glTexSubImage2D( target, 0, 0, 0, image.width(), image.height(), QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.constBits() ); } else { f.glTexImage2D( target, 0, QOpenGLTexture::RGBA8_UNorm, image.width(), image.height(), 0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.constBits() ); } f.glBindTexture( target, oldTexture ); return textureId; } QSGTexture* QskTextureRenderer::textureFromId( QQuickWindow* window, uint textureId, const QSize& size ) { const auto flags = static_cast< QQuickWindow::CreateTextureOptions >( QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture ); QSGTexture* texture; #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) texture = QNativeInterface::QSGOpenGLTexture::fromNative( textureId, window, size, flags ); #else const int nativeLayout = 0; // VkImageLayout in case of Vulkan texture = window->createTextureFromNativeObject( QQuickWindow::NativeObjectTexture, &textureId, nativeLayout, size, flags ); #endif return texture; } uint QskTextureRenderer::createTexture( QQuickWindow* window, RenderMode renderMode, const QSize& size, PaintHelper* helper ) { #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) // Qt6.0.0 is buggy when using FBOs. So let's disable it for the moment TODO ... renderMode = Raster; #endif if ( renderMode != Raster ) { if ( !qskHasOpenGLRenderer( window ) ) renderMode = Raster; } if ( renderMode == AutoDetect ) { if ( qskSetup->testItemUpdateFlag( QskQuickItem::PreferRasterForTextures ) ) renderMode = Raster; else renderMode = OpenGL; } if ( renderMode == Raster ) return qskCreateTextureRaster( window, size, helper ); else return qskCreateTextureOpenGL( window, size, helper ); } uint QskTextureRenderer::createTextureFromGraphic( QQuickWindow* window, RenderMode renderMode, const QSize& size, const QskGraphic& graphic, const QskColorFilter& colorFilter, Qt::AspectRatioMode aspectRatioMode ) { class PaintHelper : public QskTextureRenderer::PaintHelper { public: PaintHelper( const QskGraphic& graphic, const QskColorFilter& filter, Qt::AspectRatioMode aspectRatioMode ) : m_graphic( graphic ) , m_filter( filter ) , m_aspectRatioMode( aspectRatioMode ) { } void paint( QPainter* painter, const QSize& size ) override { const QRect rect( 0, 0, size.width(), size.height() ); m_graphic.render( painter, rect, m_filter, m_aspectRatioMode ); } private: const QskGraphic& m_graphic; const QskColorFilter& m_filter; const Qt::AspectRatioMode m_aspectRatioMode; }; PaintHelper helper( graphic, colorFilter, aspectRatioMode ); return createTexture( window, renderMode, size, &helper ); }