diff options
Diffstat (limited to 'src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp')
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp | 737 |
1 files changed, 737 insertions, 0 deletions
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp new file mode 100644 index 0000000000..3351486bc6 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp @@ -0,0 +1,737 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgd3d12builtinmaterials_p.h" +#include "qsgd3d12rendercontext_p.h" +#include <QQuickWindow> +#include <QtCore/qmath.h> +#include <QtGui/private/qfixed_p.h> + +#include "vs_vertexcolor.hlslh" +#include "ps_vertexcolor.hlslh" +#include "vs_flatcolor.hlslh" +#include "ps_flatcolor.hlslh" +#include "vs_smoothcolor.hlslh" +#include "ps_smoothcolor.hlslh" +#include "vs_texture.hlslh" +#include "ps_texture.hlslh" +#include "vs_smoothtexture.hlslh" +#include "ps_smoothtexture.hlslh" +#include "vs_textmask.hlslh" +#include "ps_textmask24.hlslh" +#include "ps_textmask32.hlslh" +#include "ps_textmask8.hlslh" +#include "vs_styledtext.hlslh" +#include "ps_styledtext.hlslh" +#include "vs_outlinedtext.hlslh" +#include "ps_outlinedtext.hlslh" + +QT_BEGIN_NAMESPACE + +// NB! In HLSL constant buffer data is packed into 4-byte boundaries and, more +// importantly, it is packed so that it does not cross a 16-byte (float4) +// boundary. Hence the need for padding in some cases. + +static inline QVector4D qsg_premultiply(const QVector4D &c, float globalOpacity) +{ + const float o = c.w() * globalOpacity; + return QVector4D(c.x() * o, c.y() * o, c.z() * o, o); +} + +static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) +{ + const float o = c.alphaF() * globalOpacity; + return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); +} + +static inline int qsg_colorDiff(const QVector4D &a, const QVector4D &b) +{ + if (a.x() != b.x()) + return a.x() > b.x() ? 1 : -1; + if (a.y() != b.y()) + return a.y() > b.y() ? 1 : -1; + if (a.z() != b.z()) + return a.z() > b.z() ? 1 : -1; + if (a.w() != b.w()) + return a.w() > b.w() ? 1 : -1; + return 0; +} + +QSGMaterialType QSGD3D12VertexColorMaterial::mtype; + +QSGMaterialType *QSGD3D12VertexColorMaterial::type() const +{ + return &QSGD3D12VertexColorMaterial::mtype; +} + +int QSGD3D12VertexColorMaterial::compare(const QSGMaterial *other) const +{ + Q_UNUSED(other); + Q_ASSERT(other && type() == other->type()); + // As the vertex color material has all its state in the vertex attributes + // defined by the geometry, all such materials will be equal. + return 0; +} + +static const int VERTEX_COLOR_CB_SIZE_0 = 16 * sizeof(float); // float4x4 +static const int VERTEX_COLOR_CB_SIZE_1 = sizeof(float); // float +static const int VERTEX_COLOR_CB_SIZE = VERTEX_COLOR_CB_SIZE_0 + VERTEX_COLOR_CB_SIZE_1; + +int QSGD3D12VertexColorMaterial::constantBufferSize() const +{ + return QSGD3D12Engine::alignedConstantBufferSize(VERTEX_COLOR_CB_SIZE); +} + +void QSGD3D12VertexColorMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState) +{ + pipelineState->shaders.vs = g_VS_VertexColor; + pipelineState->shaders.vsSize = sizeof(g_VS_VertexColor); + pipelineState->shaders.ps = g_PS_VertexColor; + pipelineState->shaders.psSize = sizeof(g_PS_VertexColor); +} + +QSGD3D12Material::UpdateResults QSGD3D12VertexColorMaterial::updatePipeline(const QSGD3D12MaterialRenderState &state, + QSGD3D12PipelineState *, + ExtraState *, + quint8 *constantBuffer) +{ + QSGD3D12Material::UpdateResults r = 0; + quint8 *p = constantBuffer; + + if (state.isMatrixDirty()) { + memcpy(p, state.combinedMatrix().constData(), VERTEX_COLOR_CB_SIZE_0); + r |= UpdatedConstantBuffer; + } + p += VERTEX_COLOR_CB_SIZE_0; + + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(p, &opacity, VERTEX_COLOR_CB_SIZE_1); + r |= UpdatedConstantBuffer; + } + + return r; +} + +QSGD3D12FlatColorMaterial::QSGD3D12FlatColorMaterial() + : m_color(QColor(255, 255, 255)) +{ +} + +QSGMaterialType QSGD3D12FlatColorMaterial::mtype; + +QSGMaterialType *QSGD3D12FlatColorMaterial::type() const +{ + return &QSGD3D12FlatColorMaterial::mtype; +} + +int QSGD3D12FlatColorMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QSGD3D12FlatColorMaterial *o = static_cast<const QSGD3D12FlatColorMaterial *>(other); + return m_color.rgba() - o->color().rgba(); +} + +static const int FLAT_COLOR_CB_SIZE_0 = 16 * sizeof(float); // float4x4 +static const int FLAT_COLOR_CB_SIZE_1 = 4 * sizeof(float); // float4 +static const int FLAT_COLOR_CB_SIZE = FLAT_COLOR_CB_SIZE_0 + FLAT_COLOR_CB_SIZE_1; + +int QSGD3D12FlatColorMaterial::constantBufferSize() const +{ + return QSGD3D12Engine::alignedConstantBufferSize(FLAT_COLOR_CB_SIZE); +} + +void QSGD3D12FlatColorMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState) +{ + pipelineState->shaders.vs = g_VS_FlatColor; + pipelineState->shaders.vsSize = sizeof(g_VS_FlatColor); + pipelineState->shaders.ps = g_PS_FlatColor; + pipelineState->shaders.psSize = sizeof(g_PS_FlatColor); +} + +QSGD3D12Material::UpdateResults QSGD3D12FlatColorMaterial::updatePipeline(const QSGD3D12MaterialRenderState &state, + QSGD3D12PipelineState *, + ExtraState *, + quint8 *constantBuffer) +{ + QSGD3D12Material::UpdateResults r = 0; + quint8 *p = constantBuffer; + + if (state.isMatrixDirty()) { + memcpy(p, state.combinedMatrix().constData(), FLAT_COLOR_CB_SIZE_0); + r |= UpdatedConstantBuffer; + } + p += FLAT_COLOR_CB_SIZE_0; + + const QVector4D color = qsg_premultiply(m_color, state.opacity()); + const float f[] = { color.x(), color.y(), color.z(), color.w() }; + if (state.isOpacityDirty() || memcmp(p, f, FLAT_COLOR_CB_SIZE_1)) { + memcpy(p, f, FLAT_COLOR_CB_SIZE_1); + r |= UpdatedConstantBuffer; + } + + return r; +} + +void QSGD3D12FlatColorMaterial::setColor(const QColor &color) +{ + m_color = color; + setFlag(Blending, m_color.alpha() != 0xFF); +} + +QSGD3D12SmoothColorMaterial::QSGD3D12SmoothColorMaterial() +{ + setFlag(RequiresFullMatrixExceptTranslate, true); + setFlag(Blending, true); +} + +QSGMaterialType QSGD3D12SmoothColorMaterial::mtype; + +QSGMaterialType *QSGD3D12SmoothColorMaterial::type() const +{ + return &QSGD3D12SmoothColorMaterial::mtype; +} + +int QSGD3D12SmoothColorMaterial::compare(const QSGMaterial *other) const +{ + Q_UNUSED(other); + Q_ASSERT(other && type() == other->type()); + return 0; +} + +static const int SMOOTH_COLOR_CB_SIZE_0 = 16 * sizeof(float); // float4x4 +static const int SMOOTH_COLOR_CB_SIZE_1 = sizeof(float); // float +static const int SMOOTH_COLOR_CB_SIZE_2 = 2 * sizeof(float); // float2 +static const int SMOOTH_COLOR_CB_SIZE = SMOOTH_COLOR_CB_SIZE_0 + SMOOTH_COLOR_CB_SIZE_1 + SMOOTH_COLOR_CB_SIZE_2; + +int QSGD3D12SmoothColorMaterial::constantBufferSize() const +{ + return QSGD3D12Engine::alignedConstantBufferSize(SMOOTH_COLOR_CB_SIZE); +} + +void QSGD3D12SmoothColorMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState) +{ + pipelineState->shaders.vs = g_VS_SmoothColor; + pipelineState->shaders.vsSize = sizeof(g_VS_SmoothColor); + pipelineState->shaders.ps = g_PS_SmoothColor; + pipelineState->shaders.psSize = sizeof(g_PS_SmoothColor); +} + +QSGD3D12Material::UpdateResults QSGD3D12SmoothColorMaterial::updatePipeline(const QSGD3D12MaterialRenderState &state, + QSGD3D12PipelineState *, + ExtraState *, + quint8 *constantBuffer) +{ + QSGD3D12Material::UpdateResults r = 0; + quint8 *p = constantBuffer; + + if (state.isMatrixDirty()) { + memcpy(p, state.combinedMatrix().constData(), SMOOTH_COLOR_CB_SIZE_0); + r |= UpdatedConstantBuffer; + } + p += SMOOTH_COLOR_CB_SIZE_0; + + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(p, &opacity, SMOOTH_COLOR_CB_SIZE_1); + r |= UpdatedConstantBuffer; + } + p += SMOOTH_COLOR_CB_SIZE_1; + + if (state.isMatrixDirty()) { + const QRect viewport = state.viewportRect(); + const float v[] = { 2.0f / viewport.width(), 2.0f / viewport.height() }; + memcpy(p, v, SMOOTH_COLOR_CB_SIZE_2); + r |= UpdatedConstantBuffer; + } + + return r; +} + +QSGMaterialType QSGD3D12TextureMaterial::mtype; + +QSGMaterialType *QSGD3D12TextureMaterial::type() const +{ + return &QSGD3D12TextureMaterial::mtype; +} + +int QSGD3D12TextureMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QSGD3D12TextureMaterial *o = static_cast<const QSGD3D12TextureMaterial *>(other); + if (int diff = m_texture->textureId() - o->texture()->textureId()) + return diff; + return int(m_filtering) - int(o->m_filtering); +} + +static const int TEXTURE_CB_SIZE_0 = 16 * sizeof(float); // float4x4 +static const int TEXTURE_CB_SIZE_1 = sizeof(float); // float +static const int TEXTURE_CB_SIZE = TEXTURE_CB_SIZE_0 + TEXTURE_CB_SIZE_1; + +int QSGD3D12TextureMaterial::constantBufferSize() const +{ + return QSGD3D12Engine::alignedConstantBufferSize(TEXTURE_CB_SIZE); +} + +void QSGD3D12TextureMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState) +{ + pipelineState->shaders.vs = g_VS_Texture; + pipelineState->shaders.vsSize = sizeof(g_VS_Texture); + pipelineState->shaders.ps = g_PS_Texture; + pipelineState->shaders.psSize = sizeof(g_PS_Texture); + + pipelineState->shaders.rootSig.textureViewCount = 1; +} + +QSGD3D12Material::UpdateResults QSGD3D12TextureMaterial::updatePipeline(const QSGD3D12MaterialRenderState &state, + QSGD3D12PipelineState *pipelineState, + ExtraState *, + quint8 *constantBuffer) +{ + QSGD3D12Material::UpdateResults r = 0; + quint8 *p = constantBuffer; + + if (state.isMatrixDirty()) { + memcpy(p, state.combinedMatrix().constData(), TEXTURE_CB_SIZE_0); + r |= UpdatedConstantBuffer; + } + p += TEXTURE_CB_SIZE_0; + + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(p, &opacity, TEXTURE_CB_SIZE_1); + r |= UpdatedConstantBuffer; + } + + Q_ASSERT(m_texture); + m_texture->setFiltering(m_filtering); + m_texture->setMipmapFiltering(m_mipmap_filtering); + m_texture->setHorizontalWrapMode(m_horizontal_wrap); + m_texture->setVerticalWrapMode(m_vertical_wrap); + + QSGD3D12TextureView &tv(pipelineState->shaders.rootSig.textureViews[0]); + if (m_filtering == QSGTexture::Linear) + tv.filter = m_mipmap_filtering == QSGTexture::Linear + ? QSGD3D12TextureView::FilterLinear : QSGD3D12TextureView::FilterMinMagLinearMipNearest; + else + tv.filter = m_mipmap_filtering == QSGTexture::Linear + ? QSGD3D12TextureView::FilterMinMagNearestMipLinear : QSGD3D12TextureView::FilterNearest; + tv.addressModeHoriz = m_horizontal_wrap == QSGTexture::ClampToEdge ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap; + tv.addressModeVert = m_vertical_wrap == QSGTexture::ClampToEdge ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap; + + m_texture->bind(); + + return r; +} + +void QSGD3D12TextureMaterial::setTexture(QSGTexture *texture) +{ + m_texture = texture; + setFlag(Blending, m_texture ? m_texture->hasAlphaChannel() : false); +} + +QSGD3D12SmoothTextureMaterial::QSGD3D12SmoothTextureMaterial() +{ + setFlag(RequiresFullMatrixExceptTranslate, true); + setFlag(Blending, true); +} + +QSGMaterialType QSGD3D12SmoothTextureMaterial::mtype; + +QSGMaterialType *QSGD3D12SmoothTextureMaterial::type() const +{ + return &QSGD3D12SmoothTextureMaterial::mtype; +} + +int QSGD3D12SmoothTextureMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QSGD3D12SmoothTextureMaterial *o = static_cast<const QSGD3D12SmoothTextureMaterial *>(other); + if (int diff = m_texture->textureId() - o->texture()->textureId()) + return diff; + return int(m_filtering) - int(o->m_filtering); +} + +static const int SMOOTH_TEXTURE_CB_SIZE_0 = 16 * sizeof(float); // float4x4 +static const int SMOOTH_TEXTURE_CB_SIZE_1 = sizeof(float); // float +static const int SMOOTH_TEXTURE_CB_SIZE_2 = 2 * sizeof(float); // float2 +static const int SMOOTH_TEXTURE_CB_SIZE = SMOOTH_TEXTURE_CB_SIZE_0 + SMOOTH_TEXTURE_CB_SIZE_1 + SMOOTH_TEXTURE_CB_SIZE_2; + +int QSGD3D12SmoothTextureMaterial::constantBufferSize() const +{ + return QSGD3D12Engine::alignedConstantBufferSize(SMOOTH_TEXTURE_CB_SIZE); +} + +void QSGD3D12SmoothTextureMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState) +{ + pipelineState->shaders.vs = g_VS_SmoothTexture; + pipelineState->shaders.vsSize = sizeof(g_VS_SmoothTexture); + pipelineState->shaders.ps = g_PS_SmoothTexture; + pipelineState->shaders.psSize = sizeof(g_PS_SmoothTexture); + + pipelineState->shaders.rootSig.textureViewCount = 1; +} + +QSGD3D12Material::UpdateResults QSGD3D12SmoothTextureMaterial::updatePipeline(const QSGD3D12MaterialRenderState &state, + QSGD3D12PipelineState *pipelineState, + ExtraState *, + quint8 *constantBuffer) +{ + QSGD3D12Material::UpdateResults r = 0; + quint8 *p = constantBuffer; + + if (state.isMatrixDirty()) { + memcpy(p, state.combinedMatrix().constData(), SMOOTH_TEXTURE_CB_SIZE_0); + r |= UpdatedConstantBuffer; + } + p += SMOOTH_TEXTURE_CB_SIZE_0; + + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(p, &opacity, SMOOTH_TEXTURE_CB_SIZE_1); + r |= UpdatedConstantBuffer; + } + p += SMOOTH_TEXTURE_CB_SIZE_1; + + if (state.isMatrixDirty()) { + const QRect viewport = state.viewportRect(); + const float v[] = { 2.0f / viewport.width(), 2.0f / viewport.height() }; + memcpy(p, v, SMOOTH_TEXTURE_CB_SIZE_2); + r |= UpdatedConstantBuffer; + } + + Q_ASSERT(m_texture); + m_texture->setFiltering(m_filtering); + m_texture->setMipmapFiltering(m_mipmap_filtering); + m_texture->setHorizontalWrapMode(m_horizontal_wrap); + m_texture->setVerticalWrapMode(m_vertical_wrap); + + QSGD3D12TextureView &tv(pipelineState->shaders.rootSig.textureViews[0]); + if (m_filtering == QSGTexture::Linear) + tv.filter = m_mipmap_filtering == QSGTexture::Linear + ? QSGD3D12TextureView::FilterLinear : QSGD3D12TextureView::FilterMinMagLinearMipNearest; + else + tv.filter = m_mipmap_filtering == QSGTexture::Linear + ? QSGD3D12TextureView::FilterMinMagNearestMipLinear : QSGD3D12TextureView::FilterNearest; + tv.addressModeHoriz = m_horizontal_wrap == QSGTexture::ClampToEdge ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap; + tv.addressModeVert = m_vertical_wrap == QSGTexture::ClampToEdge ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap; + + m_texture->bind(); + + return r; +} + +QSGD3D12TextMaterial::QSGD3D12TextMaterial(StyleType styleType, QSGD3D12RenderContext *rc, + const QRawFont &font, QFontEngine::GlyphFormat glyphFormat) + : m_styleType(styleType), + m_font(font), + m_rc(rc) +{ + setFlag(Blending, true); + + QRawFontPrivate *fontD = QRawFontPrivate::get(m_font); + if (QFontEngine *fontEngine = fontD->fontEngine) { + if (glyphFormat == QFontEngine::Format_None) + glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None + ? fontEngine->glyphFormat : QFontEngine::Format_A32; + + QSGD3D12Engine *d3dengine = rc->engine(); + const float devicePixelRatio = d3dengine->windowDevicePixelRatio(); + QTransform glyphCacheTransform = QTransform::fromScale(devicePixelRatio, devicePixelRatio); + if (!fontEngine->supportsTransformation(glyphCacheTransform)) + glyphCacheTransform = QTransform(); + + m_glyphCache = fontEngine->glyphCache(d3dengine, glyphFormat, glyphCacheTransform); + if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) { + m_glyphCache = new QSGD3D12GlyphCache(d3dengine, glyphFormat, glyphCacheTransform); + fontEngine->setGlyphCache(d3dengine, m_glyphCache.data()); + rc->registerFontengineForCleanup(fontEngine); + } + } +} + +QSGMaterialType QSGD3D12TextMaterial::mtype[QSGD3D12TextMaterial::NTextMaterialTypes]; + +QSGMaterialType *QSGD3D12TextMaterial::type() const +{ + // Format_A32 has special blend settings and therefore two materials with + // the same style but different formats where one is A32 are treated as + // different. This way the renderer can manage the pipeline state properly. + const int matStyle = m_styleType * 2; + const int matFormat = glyphCache()->glyphFormat() != QFontEngine::Format_A32 ? 0 : 1; + return &QSGD3D12TextMaterial::mtype[matStyle + matFormat]; +} + +int QSGD3D12TextMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QSGD3D12TextMaterial *o = static_cast<const QSGD3D12TextMaterial *>(other); + if (m_styleType != o->m_styleType) + return m_styleType - o->m_styleType; + if (m_glyphCache != o->m_glyphCache) + return m_glyphCache.data() < o->m_glyphCache.data() ? -1 : 1; + if (m_styleShift != o->m_styleShift) + return m_styleShift.y() - o->m_styleShift.y(); + int styleColorDiff = qsg_colorDiff(m_styleColor, o->m_styleColor); + if (styleColorDiff) + return styleColorDiff; + return qsg_colorDiff(m_color, o->m_color); +} + +static const int TEXT_CB_SIZE_0 = 16 * sizeof(float); // float4x4 mvp +static const int TEXT_CB_SIZE_1 = 2 * sizeof(float); // float2 textureScale +static const int TEXT_CB_SIZE_2 = sizeof(float); // float dpr +static const int TEXT_CB_SIZE_3 = sizeof(float); // float color +static const int TEXT_CB_SIZE_4 = 4 * sizeof(float); // float4 colorVec +static const int TEXT_CB_SIZE_5 = 2 * sizeof(float); // float2 shift +static const int TEXT_CB_SIZE_5_PADDING = 2 * sizeof(float); // float2 padding (the next float4 would cross the 16-byte boundary) +static const int TEXT_CB_SIZE_6 = 4 * sizeof(float); // float4 styleColor +static const int TEXT_CB_SIZE = TEXT_CB_SIZE_0 + TEXT_CB_SIZE_1 + TEXT_CB_SIZE_2 + TEXT_CB_SIZE_3 + + TEXT_CB_SIZE_4 + TEXT_CB_SIZE_5 + TEXT_CB_SIZE_5_PADDING + TEXT_CB_SIZE_6; + +int QSGD3D12TextMaterial::constantBufferSize() const +{ + return QSGD3D12Engine::alignedConstantBufferSize(TEXT_CB_SIZE); +} + +void QSGD3D12TextMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState) +{ + if (m_styleType == Normal) { + pipelineState->shaders.vs = g_VS_TextMask; + pipelineState->shaders.vsSize = sizeof(g_VS_TextMask); + switch (glyphCache()->glyphFormat()) { + case QFontEngine::Format_A32: + pipelineState->shaders.ps = g_PS_TextMask24; + pipelineState->shaders.psSize = sizeof(g_PS_TextMask24); + break; + case QFontEngine::Format_ARGB: + pipelineState->shaders.ps = g_PS_TextMask32; + pipelineState->shaders.psSize = sizeof(g_PS_TextMask32); + break; + default: + pipelineState->shaders.ps = g_PS_TextMask8; + pipelineState->shaders.psSize = sizeof(g_PS_TextMask8); + break; + } + } else if (m_styleType == Outlined) { + pipelineState->shaders.vs = g_VS_OutlinedText; + pipelineState->shaders.vsSize = sizeof(g_VS_OutlinedText); + pipelineState->shaders.ps = g_PS_OutlinedText; + pipelineState->shaders.psSize = sizeof(g_PS_OutlinedText); + } else { + pipelineState->shaders.vs = g_VS_StyledText; + pipelineState->shaders.vsSize = sizeof(g_VS_StyledText); + pipelineState->shaders.ps = g_PS_StyledText; + pipelineState->shaders.psSize = sizeof(g_PS_StyledText); + } + + pipelineState->shaders.rootSig.textureViewCount = 1; +} + +QSGD3D12Material::UpdateResults QSGD3D12TextMaterial::updatePipeline(const QSGD3D12MaterialRenderState &state, + QSGD3D12PipelineState *pipelineState, + ExtraState *extraState, + quint8 *constantBuffer) +{ + QSGD3D12Material::UpdateResults r = 0; + quint8 *p = constantBuffer; + + if (glyphCache()->glyphFormat() == QFontEngine::Format_A32) { + // can freely change the state due to the way type() works + pipelineState->blend = QSGD3D12PipelineState::BlendColor; + extraState->blendFactor = m_color; + r |= UpdatedBlendFactor; // must be set always as this affects the command list + } + + if (state.isMatrixDirty()) { + memcpy(p, state.combinedMatrix().constData(), TEXT_CB_SIZE_0); + r |= UpdatedConstantBuffer; + } + p += TEXT_CB_SIZE_0; + + const QSize sz = glyphCache()->currentSize(); + const float textureScale[] = { 1.0f / sz.width(), 1.0f / sz.height() }; + if (state.isCachedMaterialDataDirty() || memcmp(p, textureScale, TEXT_CB_SIZE_1)) { + memcpy(p, textureScale, TEXT_CB_SIZE_1); + r |= UpdatedConstantBuffer; + } + p += TEXT_CB_SIZE_1; + + const float dpr = m_rc->engine()->windowDevicePixelRatio(); + if (state.isCachedMaterialDataDirty() || memcmp(p, &dpr, TEXT_CB_SIZE_2)) { + memcpy(p, &dpr, TEXT_CB_SIZE_2); + r |= UpdatedConstantBuffer; + } + p += TEXT_CB_SIZE_2; + + if (glyphCache()->glyphFormat() == QFontEngine::Format_A32) { + const QVector4D color = qsg_premultiply(m_color, state.opacity()); + const float alpha = color.w(); + if (state.isOpacityDirty() || memcmp(p, &alpha, TEXT_CB_SIZE_3)) { + memcpy(p, &alpha, TEXT_CB_SIZE_3); + r |= UpdatedConstantBuffer; + } + } else if (glyphCache()->glyphFormat() == QFontEngine::Format_ARGB) { + const float opacity = m_color.w() * state.opacity(); + if (state.isOpacityDirty() || memcmp(p, &opacity, TEXT_CB_SIZE_3)) { + memcpy(p, &opacity, TEXT_CB_SIZE_3); + r |= UpdatedConstantBuffer; + } + } else { + const QVector4D color = qsg_premultiply(m_color, state.opacity()); + const float f[] = { color.x(), color.y(), color.z(), color.w() }; + if (state.isOpacityDirty() || memcmp(p, f, TEXT_CB_SIZE_4)) { + memcpy(p + TEXT_CB_SIZE_3, f, TEXT_CB_SIZE_4); + r |= UpdatedConstantBuffer; + } + } + p += TEXT_CB_SIZE_3 + TEXT_CB_SIZE_4; + + if (m_styleType == Styled) { + const float f[] = { m_styleShift.x(), m_styleShift.y() }; + if (state.isCachedMaterialDataDirty() || memcmp(p, f, TEXT_CB_SIZE_5)) { + memcpy(p, f, TEXT_CB_SIZE_5); + r |= UpdatedConstantBuffer; + } + } + p += TEXT_CB_SIZE_5 + TEXT_CB_SIZE_5_PADDING; + + if (m_styleType == Styled || m_styleType == Outlined) { + const QVector4D color = qsg_premultiply(m_styleColor, state.opacity()); + const float f[] = { color.x(), color.y(), color.z(), color.w() }; + if (state.isOpacityDirty() || memcmp(p, f, TEXT_CB_SIZE_6)) { + memcpy(p, f, TEXT_CB_SIZE_6); + r |= UpdatedConstantBuffer; + } + } + + QSGD3D12TextureView &tv(pipelineState->shaders.rootSig.textureViews[0]); + tv.filter = QSGD3D12TextureView::FilterNearest; + tv.addressModeHoriz = QSGD3D12TextureView::AddressClamp; + tv.addressModeVert = QSGD3D12TextureView::AddressClamp; + + glyphCache()->useTexture(); + + return r; +} + +void QSGD3D12TextMaterial::populate(const QPointF &p, + const QVector<quint32> &glyphIndexes, + const QVector<QPointF> &glyphPositions, + QSGGeometry *geometry, + QRectF *boundingRect, + QPointF *baseLine, + const QMargins &margins) +{ + Q_ASSERT(m_font.isValid()); + QVector<QFixedPoint> fixedPointPositions; + const int glyphPositionsSize = glyphPositions.size(); + fixedPointPositions.reserve(glyphPositionsSize); + for (int i=0; i < glyphPositionsSize; ++i) + fixedPointPositions.append(QFixedPoint::fromPointF(glyphPositions.at(i))); + + QSGD3D12GlyphCache *cache = glyphCache(); + QRawFontPrivate *fontD = QRawFontPrivate::get(m_font); + cache->populate(fontD->fontEngine, glyphIndexes.size(), glyphIndexes.constData(), + fixedPointPositions.data()); + cache->fillInPendingGlyphs(); + + int margin = fontD->fontEngine->glyphMargin(cache->glyphFormat()); + + float glyphCacheScaleX = cache->transform().m11(); + float glyphCacheScaleY = cache->transform().m22(); + float glyphCacheInverseScaleX = 1.0 / glyphCacheScaleX; + float glyphCacheInverseScaleY = 1.0 / glyphCacheScaleY; + + Q_ASSERT(geometry->indexType() == QSGGeometry::UnsignedShortType); + geometry->allocate(glyphIndexes.size() * 4, glyphIndexes.size() * 6); + QVector4D *vp = reinterpret_cast<QVector4D *>(geometry->vertexDataAsTexturedPoint2D()); + Q_ASSERT(geometry->sizeOfVertex() == sizeof(QVector4D)); + ushort *ip = geometry->indexDataAsUShort(); + + QPointF position(p.x(), p.y() - m_font.ascent()); + bool supportsSubPixelPositions = fontD->fontEngine->supportsSubPixelPositions(); + for (int i = 0; i < glyphIndexes.size(); ++i) { + QFixed subPixelPosition; + if (supportsSubPixelPositions) + subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPositions.at(i).x())); + + QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition); + const QTextureGlyphCache::Coord &c = cache->coords.value(glyph); + + QPointF glyphPosition = glyphPositions.at(i) + position; + float x = (qFloor(glyphPosition.x() * glyphCacheScaleX) * glyphCacheInverseScaleX) + + (c.baseLineX * glyphCacheInverseScaleX) - margin; + float y = (qRound(glyphPosition.y() * glyphCacheScaleY) * glyphCacheInverseScaleY) + - (c.baseLineY * glyphCacheInverseScaleY) - margin; + + float w = c.w * glyphCacheInverseScaleX; + float h = c.h * glyphCacheInverseScaleY; + + *boundingRect |= QRectF(x + margin, y + margin, w, h); + + float cx1 = x - margins.left(); + float cx2 = x + w + margins.right(); + float cy1 = y - margins.top(); + float cy2 = y + h + margins.bottom(); + + float tx1 = c.x - margins.left(); + float tx2 = c.x + c.w + margins.right(); + float ty1 = c.y - margins.top(); + float ty2 = c.y + c.h + margins.bottom(); + + if (baseLine->isNull()) + *baseLine = glyphPosition; + + vp[4 * i + 0] = QVector4D(cx1, cy1, tx1, ty1); + vp[4 * i + 1] = QVector4D(cx2, cy1, tx2, ty1); + vp[4 * i + 2] = QVector4D(cx1, cy2, tx1, ty2); + vp[4 * i + 3] = QVector4D(cx2, cy2, tx2, ty2); + + int o = i * 4; + ip[6 * i + 0] = o + 0; + ip[6 * i + 1] = o + 2; + ip[6 * i + 2] = o + 3; + ip[6 * i + 3] = o + 3; + ip[6 * i + 4] = o + 1; + ip[6 * i + 5] = o + 0; + } +} + +QT_END_NAMESPACE |