blob: 312e8c19cda74c7f077e717b88199ebf922ab5b1 [file] [log] [blame]
/****************************************************************************
**
** 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_rc(rc),
m_font(font)
{
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