diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b626e406a..a6ff444ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -97,6 +97,7 @@ list(APPEND SOURCES list(APPEND HEADERS nodes/QskArcNode.h + nodes/QskBasicLinesNode.h nodes/QskBoxNode.h nodes/QskBoxClipNode.h nodes/QskBoxFillNode.h @@ -133,6 +134,7 @@ list(APPEND PRIVATE_HEADERS list(APPEND SOURCES nodes/QskArcNode.cpp + nodes/QskBasicLinesNode.cpp nodes/QskBoxNode.cpp nodes/QskBoxClipNode.cpp nodes/QskBoxFillNode.cpp diff --git a/src/nodes/QskBasicLinesNode.cpp b/src/nodes/QskBasicLinesNode.cpp new file mode 100644 index 000000000..a4e41df09 --- /dev/null +++ b/src/nodes/QskBasicLinesNode.cpp @@ -0,0 +1,190 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskBasicLinesNode.h" + +#include +#include +#include + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +namespace +{ + class Material final : public QSGMaterial + { + public: + Material(); + + QSGMaterialShader* createShader( QSGRendererInterface::RenderMode ) const override; + + QSGMaterialType* type() const override; + + int compare( const QSGMaterial* other ) const override; + + QVector4D m_color = QVector4D{ 0, 0, 0, 1 }; + Qt::Orientations m_pixelAlignment; + }; + + class ShaderRhi final : public QSGMaterialShader + { + public: + + ShaderRhi() + { + const QString root( ":/qskinny/shaders/" ); + + setShaderFileName( VertexStage, root + "crisplines.vert.qsb" ); + setShaderFileName( FragmentStage, root + "crisplines.frag.qsb" ); + } + + bool updateUniformData( RenderState& state, + QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) override + { + auto matOld = static_cast< Material* >( oldMaterial ); + auto matNew = static_cast< Material* >( newMaterial ); + + Q_ASSERT( state.uniformData()->size() >= 88 ); + + auto data = state.uniformData()->data(); + bool changed = false; + + const auto matrix = state.combinedMatrix(); + + if ( state.isMatrixDirty() ) + { + memcpy( data + 0, matrix.constData(), 64 ); + changed = true; + } + + if ( ( matOld == nullptr ) || ( matNew->m_color != matOld->m_color ) ) + { + memcpy( data + 64, &matNew->m_color, 16 ); + changed = true; + } + + if ( state.isMatrixDirty() || ( matOld == nullptr ) + || ( matNew->m_pixelAlignment != matOld->m_pixelAlignment ) ) + { + /* + We do not need to upload the size as it is available + from the matrix. But the shader needs to know wether to + round or not TODO ... + */ + QVector2D size; + + if ( matNew->m_pixelAlignment & Qt::Horizontal ) + size.setX( 2.0 / matrix( 0, 0 ) ); + + if ( matNew->m_pixelAlignment & Qt::Vertical ) + size.setY( -2.0 / matrix( 1, 1 ) ); + + memcpy( data + 80, &size, 8 ); + changed = true; + } + + return changed; + } + }; +} + +Material::Material() +{ +} + +QSGMaterialShader* Material::createShader( QSGRendererInterface::RenderMode ) const +{ + return new ShaderRhi(); +} + +QSGMaterialType* Material::type() const +{ + static QSGMaterialType staticType; + return &staticType; +} + +int Material::compare( const QSGMaterial* other ) const +{ + auto material = static_cast< const Material* >( other ); + + if ( material->m_color == m_color ) + return 0; + + return QSGMaterial::compare( other ); +} + +class QskBasicLinesNodePrivate final : public QSGGeometryNodePrivate +{ + public: + QskBasicLinesNodePrivate() + : geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) + { + geometry.setDrawingMode( QSGGeometry::DrawLines ); + } + + QSGGeometry geometry; + Material material; +}; + +QskBasicLinesNode::QskBasicLinesNode() + : QSGGeometryNode( *new QskBasicLinesNodePrivate ) +{ + Q_D( QskBasicLinesNode ); + + setGeometry( &d->geometry ); + setMaterial( &d->material ); +} + +QskBasicLinesNode::~QskBasicLinesNode() +{ +} + +void QskBasicLinesNode::setPixelAlignment( Qt::Orientations orientations ) +{ + Q_D( QskBasicLinesNode ); + + if ( orientations != d->material.m_pixelAlignment ) + { + d->material.m_pixelAlignment = orientations; + markDirty( QSGNode::DirtyMaterial ); + } +} + +Qt::Orientations QskBasicLinesNode::pixelAlignment() const +{ + return d_func()->material.m_pixelAlignment; +} + +void QskBasicLinesNode::setColor( const QColor& color ) +{ + Q_D( QskBasicLinesNode ); + + const auto a = color.alphaF(); + + const QVector4D c( color.redF() * a, color.greenF() * a, color.blueF() * a, a ); + + if ( c != d->material.m_color ) + { + d->material.m_color = c; + markDirty( QSGNode::DirtyMaterial ); + } +} + +void QskBasicLinesNode::setLineWidth( float lineWidth ) +{ + Q_D( QskBasicLinesNode ); + + lineWidth = std::max( lineWidth, 0.0f ); + if( lineWidth != d->geometry.lineWidth() ) + d->geometry.setLineWidth( lineWidth ); +} + +float QskBasicLinesNode::lineWidth() const +{ + return d_func()->geometry.lineWidth(); +} + diff --git a/src/nodes/QskBasicLinesNode.h b/src/nodes/QskBasicLinesNode.h new file mode 100644 index 000000000..607c9cc52 --- /dev/null +++ b/src/nodes/QskBasicLinesNode.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_BASIC_LINES_NODE_H +#define QSK_BASIC_LINES_NODE_H + +#include "QskGlobal.h" + +#include +#include + +class QColor; + +class QskBasicLinesNodePrivate; + +/* + A node for stippled or solid lines. + For the moment limited to horizontal/vertical lines: TODO + */ +class QSK_EXPORT QskBasicLinesNode : public QSGGeometryNode +{ + using Inherited = QSGGeometryNode; + + public: + QskBasicLinesNode(); + ~QskBasicLinesNode() override; + + void setPixelAlignment( Qt::Orientations ); + Qt::Orientations pixelAlignment() const; + + void setColor( const QColor& ); + + void setLineWidth( float ); + float lineWidth() const; + + private: + Q_DECLARE_PRIVATE( QskBasicLinesNode ) +}; + +#endif diff --git a/src/nodes/shaders.qrc b/src/nodes/shaders.qrc index 0301b9916..d87dec16c 100644 --- a/src/nodes/shaders.qrc +++ b/src/nodes/shaders.qrc @@ -22,5 +22,8 @@ shaders/gradientlinear.vert shaders/gradientlinear.frag + shaders/crisplines.vert.qsb + shaders/crisplines.frag.qsb + diff --git a/src/nodes/shaders/crisplines-vulkan.frag b/src/nodes/shaders/crisplines-vulkan.frag new file mode 100644 index 000000000..a4618fe9b --- /dev/null +++ b/src/nodes/shaders/crisplines-vulkan.frag @@ -0,0 +1,15 @@ +#version 440 + +layout( location = 0 ) out vec4 fragColor; + +layout( std140, binding = 0 ) uniform buf +{ + mat4 matrix; + vec4 color; + vec2 size; +} ubuf; + +void main() +{ + fragColor = ubuf.color; +} diff --git a/src/nodes/shaders/crisplines-vulkan.vert b/src/nodes/shaders/crisplines-vulkan.vert new file mode 100644 index 000000000..e572cda7a --- /dev/null +++ b/src/nodes/shaders/crisplines-vulkan.vert @@ -0,0 +1,41 @@ +#version 440 + +layout( location = 0 ) in vec4 vertexCoord; + +layout( std140, binding = 0 ) uniform buf +{ + mat4 matrix; + vec4 color; + vec2 size; +} ubuf; + +out gl_PerVertex { vec4 gl_Position; }; + +float normalized( in float pos, in float scale, in float size ) +{ + return ( ( pos / size - 0.5 ) / 0.5 ) * scale; +} + +float denormalized( in float pos, in float scale, in float size ) +{ + return ( ( pos / scale ) * 0.5 + 0.5 ) * size; +} + +void main() +{ + gl_Position = ubuf.matrix * vertexCoord; + + if ( ubuf.size.x > 0.0 ) + { + gl_Position.x = denormalized( gl_Position.x, gl_Position.w, ubuf.size.x ); + gl_Position.x = round( gl_Position.x ) + 0.5; + gl_Position.x = normalized( gl_Position.x, gl_Position.w, ubuf.size.x ); + } + + if ( ubuf.size.y > 0.0 ) + { + gl_Position.y = denormalized( gl_Position.y, gl_Position.w, ubuf.size.y ); + gl_Position.y = round( gl_Position.y ) + 0.5; + gl_Position.y = normalized( gl_Position.y, gl_Position.w, ubuf.size.y ); + } +} diff --git a/src/nodes/shaders/crisplines.frag.qsb b/src/nodes/shaders/crisplines.frag.qsb new file mode 100644 index 000000000..7d310ef43 Binary files /dev/null and b/src/nodes/shaders/crisplines.frag.qsb differ diff --git a/src/nodes/shaders/crisplines.vert.qsb b/src/nodes/shaders/crisplines.vert.qsb new file mode 100644 index 000000000..bff457692 Binary files /dev/null and b/src/nodes/shaders/crisplines.vert.qsb differ diff --git a/src/nodes/shaders/vulkan2qsb.sh b/src/nodes/shaders/vulkan2qsb.sh index 84f7f3894..22e28e10d 100755 --- a/src/nodes/shaders/vulkan2qsb.sh +++ b/src/nodes/shaders/vulkan2qsb.sh @@ -16,3 +16,6 @@ qsbcompile gradientradial-vulkan.frag qsbcompile gradientlinear-vulkan.vert qsbcompile gradientlinear-vulkan.frag + +qsbcompile crisplines-vulkan.vert +qsbcompile crisplines-vulkan.frag