qskinny/src/nodes/QskTextureNode.cpp

327 lines
7.5 KiB
C++
Raw Normal View History

2017-07-21 18:21:34 +02:00
#include "QskTextureNode.h"
2018-07-19 14:10:48 +02:00
#include <qsggeometry.h>
#include <qsgmaterial.h>
#include <qopenglfunctions.h>
2017-07-21 18:21:34 +02:00
#include <private/qsgnode_p.h>
2018-07-13 15:09:25 +02:00
#if 1
2018-07-19 14:10:48 +02:00
#include <qsurface.h>
#include <qquickwindow.h>
#include <qguiapplication.h>
#include <qscreen.h>
2018-07-13 15:09:25 +02:00
static inline qreal qskDevicePixelRatio()
{
qreal ratio = 1.0;
const auto context = QOpenGLContext::currentContext();
if ( context->surface()->surfaceClass() == QSurface::Window )
{
auto *window = static_cast< QWindow* >( context->surface() );
if ( auto* quickWindow = qobject_cast< QQuickWindow *>( window ) )
ratio = quickWindow->effectiveDevicePixelRatio();
else
ratio = window->devicePixelRatio();
}
else
{
if ( context->screen() )
ratio = context->screen()->devicePixelRatio();
else
ratio = qGuiApp->devicePixelRatio();
}
return ratio;
}
#endif
2017-07-21 18:21:34 +02:00
namespace
{
class MaterialShader final : public QSGMaterialShader
{
public:
MaterialShader( bool isOpaque );
virtual char const* const* attributeNames() const override final;
virtual void updateState( const RenderState&, QSGMaterial*, QSGMaterial* ) override final;
protected:
virtual void initialize() override final;
private:
int m_matrixId;
int m_opacityId;
const bool m_isOpaque : 1;
};
class Material final : public QSGMaterial
{
public:
Material( bool isOpaque );
virtual QSGMaterialType* type() const override;
virtual QSGMaterialShader* createShader() const override;
2017-12-07 17:04:05 +01:00
void setTextureId( uint );
uint textureId() const;
2017-07-21 18:21:34 +02:00
virtual int compare(const QSGMaterial* ) const override;
private:
2017-12-07 17:04:05 +01:00
uint m_textureId;
2017-07-21 18:21:34 +02:00
const bool m_isOpaque : 1;
};
MaterialShader::MaterialShader( bool isOpaque ):
m_isOpaque( isOpaque )
{
setShaderSourceFile(QOpenGLShader::Vertex,
QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.vert") );
setShaderSourceFile(QOpenGLShader::Fragment,
QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.frag") );
if ( !m_isOpaque )
{
setShaderSourceFile(QOpenGLShader::Fragment,
QStringLiteral(":/qt-project.org/scenegraph/shaders/texture.frag") );
}
}
char const* const* MaterialShader::attributeNames() const
{
static char const* const attr[] = { "qt_VertexPosition", "qt_VertexTexCoord", 0 };
return attr;
}
void MaterialShader::updateState( const RenderState& state,
QSGMaterial* newMaterial, QSGMaterial* oldMaterial)
{
if ( !m_isOpaque && state.isOpacityDirty() )
program()->setUniformValue( m_opacityId, state.opacity() );
auto* materialOld = static_cast< Material* >( oldMaterial );
auto* materialNew = static_cast< Material* >( newMaterial );
2018-07-13 15:09:25 +02:00
if ( ( materialOld == nullptr )
|| ( materialOld->textureId() != materialNew->textureId() ) )
2017-07-21 18:21:34 +02:00
{
auto funcs = QOpenGLContext::currentContext()->functions();
funcs->glBindTexture( GL_TEXTURE_2D, materialNew->textureId() );
}
if ( state.isMatrixDirty() )
program()->setUniformValue( m_matrixId, state.combinedMatrix() );
}
void MaterialShader::initialize()
{
m_matrixId = program()->uniformLocation("qt_Matrix");
if ( !m_isOpaque )
m_opacityId = program()->uniformLocation("opacity");
}
Material::Material( bool isOpaque ) :
m_textureId(0),
m_isOpaque( isOpaque )
{
setFlag(Blending, true ); // alpha blending
}
2017-12-07 17:04:05 +01:00
void Material::setTextureId( uint id )
2017-07-21 18:21:34 +02:00
{
m_textureId = id;
}
2017-12-07 17:04:05 +01:00
uint Material::textureId() const
2017-07-21 18:21:34 +02:00
{
return m_textureId;
}
QSGMaterialType* Material::type() const
{
if ( m_isOpaque )
{
static QSGMaterialType typeOpaque;
return &typeOpaque;
}
else
{
static QSGMaterialType type;
return &type;
}
}
QSGMaterialShader* Material::createShader() const
{
return new MaterialShader( m_isOpaque );
}
int Material::compare( const QSGMaterial* other ) const
{
const auto otherMaterial = static_cast< const Material* >( other );
2017-12-07 17:04:05 +01:00
if ( m_textureId == otherMaterial->m_textureId )
return 0;
return ( m_textureId > otherMaterial->m_textureId ) ? 1 : -1;
2017-07-21 18:21:34 +02:00
}
}
2017-12-07 17:04:05 +01:00
class QskTextureNodePrivate final : public QSGGeometryNodePrivate
2017-07-21 18:21:34 +02:00
{
public:
QskTextureNodePrivate():
geometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 ),
opaqueMaterial( true ),
material( false )
{
}
QSGGeometry geometry;
Material opaqueMaterial;
Material material;
QRectF rect;
Qt::Orientations mirrorOrientations;
};
QskTextureNode::QskTextureNode() :
QSGGeometryNode( *new QskTextureNodePrivate )
{
Q_D( QskTextureNode );
setGeometry( &d->geometry );
setMaterial( &d->material );
setOpaqueMaterial( &d->opaqueMaterial );
}
QskTextureNode::~QskTextureNode()
{
Q_D( QskTextureNode );
if ( d->material.textureId() > 0 )
{
2018-02-06 07:51:33 +01:00
/*
In certain environments we have the effect, that at
program termination the context is already gone
*/
if ( auto context = QOpenGLContext::currentContext() )
{
GLuint id = d->material.textureId();
2017-07-21 18:21:34 +02:00
2018-02-06 07:51:33 +01:00
auto funcs = context->functions();
funcs->glDeleteTextures( 1, &id );
}
2017-07-21 18:21:34 +02:00
}
}
void QskTextureNode::setRect(const QRectF& r)
{
Q_D( QskTextureNode );
if ( d->rect == r)
return;
d->rect = r;
updateTexture();
markDirty( DirtyGeometry );
}
QRectF QskTextureNode::rect() const
{
Q_D( const QskTextureNode );
return d->rect;
}
2017-12-07 17:04:05 +01:00
void QskTextureNode::setTextureId( uint textureId )
2017-07-21 18:21:34 +02:00
{
Q_D( QskTextureNode );
if ( textureId == d->material.textureId() )
return;
if ( d->material.textureId() > 0 )
{
GLuint id = d->material.textureId();
auto funcs = QOpenGLContext::currentContext()->functions();
funcs->glDeleteTextures( 1, &id );
}
d->material.setTextureId( textureId );
d->opaqueMaterial.setTextureId( textureId );
updateTexture();
DirtyState dirty = DirtyMaterial;
#if 0
// if old/new is in the atlas
dirty |= DirtyGeometry;
#endif
markDirty( dirty );
}
2017-12-07 17:04:05 +01:00
uint QskTextureNode::textureId() const
2017-07-21 18:21:34 +02:00
{
Q_D( const QskTextureNode );
return d->material.textureId();
}
void QskTextureNode::setMirrored( Qt::Orientations orientations )
{
Q_D( QskTextureNode );
if ( d->mirrorOrientations == orientations )
return;
d->mirrorOrientations = orientations;
updateTexture();
markDirty(DirtyMaterial);
}
Qt::Orientations QskTextureNode::mirrored() const
{
Q_D( const QskTextureNode );
return d->mirrorOrientations;
}
void QskTextureNode::updateTexture()
{
Q_D( QskTextureNode );
QRectF r( 0, 0, 1, 1 );
if ( d->mirrorOrientations & Qt::Horizontal )
{
r.setLeft( 1 );
r.setRight( 0 );
}
if ( d->mirrorOrientations & Qt::Vertical )
{
r.setTop( 1 );
r.setBottom( 0 );
}
2018-07-13 15:09:25 +02:00
#if 1
const qreal ratio = qskDevicePixelRatio();
const QRect rect( d->rect.x(), d->rect.y(),
d->rect.width() / ratio, d->rect.height() / ratio );
#endif
QSGGeometry::updateTexturedRectGeometry( &d->geometry, rect, r );
2017-07-21 18:21:34 +02:00
}