diff options
Diffstat (limited to 'src')
32 files changed, 2555 insertions, 1359 deletions
diff --git a/src/quick/scenegraph/adaptations/d3d12/d3d12.pri b/src/quick/scenegraph/adaptations/d3d12/d3d12.pri index ce0bced96c..8950734c56 100644 --- a/src/quick/scenegraph/adaptations/d3d12/d3d12.pri +++ b/src/quick/scenegraph/adaptations/d3d12/d3d12.pri @@ -8,7 +8,9 @@ SOURCES += \ $$PWD/qsgd3d12material.cpp \ $$PWD/qsgd3d12builtinmaterials.cpp \ $$PWD/qsgd3d12texture.cpp \ - $$PWD/qsgd3d12imagenode.cpp + $$PWD/qsgd3d12imagenode.cpp \ + $$PWD/qsgd3d12glyphnode.cpp \ + $$PWD/qsgd3d12glyphcache.cpp NO_PCH_SOURCES += \ $$PWD/qsgd3d12engine.cpp @@ -25,7 +27,9 @@ HEADERS += \ $$PWD/qsgd3d12material_p.h \ $$PWD/qsgd3d12builtinmaterials_p.h \ $$PWD/qsgd3d12texture_p.h \ - $$PWD/qsgd3d12imagenode_p.h + $$PWD/qsgd3d12imagenode_p.h \ + $$PWD/qsgd3d12glyphnode_p.h \ + $$PWD/qsgd3d12glyphcache_p.h LIBS += -ldxgi -ld3d12 diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials.cpp index d5f6246c5d..3a42008972 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials.cpp +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials.cpp @@ -38,6 +38,8 @@ ****************************************************************************/ #include "qsgd3d12builtinmaterials_p.h" +#include "qsgd3d12rendercontext_p.h" +#include <QQuickWindow> #include "vs_vertexcolor.hlslh" #include "ps_vertexcolor.hlslh" @@ -47,6 +49,8 @@ #include "ps_texture.hlslh" #include "vs_smoothtexture.hlslh" #include "ps_smoothtexture.hlslh" +#include "vs_textmask.hlslh" +#include "ps_textmask24.hlslh" QT_BEGIN_NAMESPACE @@ -336,4 +340,222 @@ QSGD3D12Material::UpdateResults QSGD3D12SmoothTextureMaterial::updatePipeline(co return r; } +static inline float qsg_device_pixel_ratio(QSGD3D12Engine *engine) +{ + // ### offscreen render target support will need changes + float devicePixelRatio = 1; + QWindow *w = engine->window(); + if (QQuickWindow *qw = qobject_cast<QQuickWindow *>(w)) + devicePixelRatio = qw->effectiveDevicePixelRatio(); + else + devicePixelRatio = w->devicePixelRatio(); + + return devicePixelRatio; +} + +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; +} + +QSGD3D12TextMaterial::QSGD3D12TextMaterial(QSGD3D12RenderContext *rc, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat) + : 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 = qsg_device_pixel_ratio(d3dengine); + 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; + +QSGMaterialType *QSGD3D12TextMaterial::type() const +{ + return &QSGD3D12TextMaterial::mtype; +} + +int QSGD3D12TextMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QSGD3D12TextMaterial *o = static_cast<const QSGD3D12TextMaterial *>(other); + if (m_glyphCache != o->m_glyphCache) + return m_glyphCache.data() < o->m_glyphCache.data() ? -1 : 1; + return qsg_colorDiff(color(), o->color()); +} + +static const int TEXT_CB_SIZE_0 = 16 * sizeof(float); // float4x4 +static const int TEXT_CB_SIZE_1 = 2 * sizeof(float); // float2 +static const int TEXT_CB_SIZE_2 = sizeof(float); // float +static const int TEXT_CB_SIZE_3 = sizeof(float); // float +static const int TEXT_CB_SIZE = TEXT_CB_SIZE_0 + TEXT_CB_SIZE_1 + TEXT_CB_SIZE_2 + TEXT_CB_SIZE_3; + +int QSGD3D12TextMaterial::constantBufferSize() const +{ + return QSGD3D12Engine::alignedConstantBufferSize(TEXT_CB_SIZE); +} + +void QSGD3D12TextMaterial::preparePipeline(QSGD3D12ShaderState *shaders) +{ + shaders->vs = g_VS_TextMask; + shaders->vsSize = sizeof(g_VS_TextMask); + shaders->ps = g_PS_TextMask24; + shaders->psSize = sizeof(g_PS_TextMask24); + + shaders->rootSig.textureViews.resize(1); +} + +QSGD3D12Material::UpdateResults QSGD3D12TextMaterial::updatePipeline(const RenderState &state, + QSGD3D12ShaderState *shaders, + quint8 *constantBuffer) +{ + QSGD3D12Material::UpdateResults r = 0; +#if 0 + quint8 *p = constantBuffer; + + if (state.isMatrixDirty()) { + memcpy(p, state.combinedMatrix().constData(), TEXT_CB_SIZE_0); + r |= UpdatedConstantBuffer; + } + p += TEXT_CB_SIZE_0; + + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(p, &opacity, TEXT_CB_SIZE_1); + r |= UpdatedConstantBuffer; + } + p += TEXT_CB_SIZE_1; + + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(p, &opacity, TEXT_CB_SIZE_2); + r |= UpdatedConstantBuffer; + } + p += TEXT_CB_SIZE_2; + + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(p, &opacity, TEXT_CB_SIZE_3); + r |= UpdatedConstantBuffer; + } + p += TEXT_CB_SIZE_3; +#endif + + QSGD3D12TextureView &tv(shaders->rootSig.textureViews[0]); + tv.filter = QSGD3D12TextureView::FilterNearest; + tv.addressModeHoriz = QSGD3D12TextureView::AddressClamp; + tv.addressModeVert = QSGD3D12TextureView::AddressClamp; + + glyphCache()->activateTexture(); + + 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::TypeUnsignedShort); + 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 diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials_p.h index 5ed65ed289..26119cc2e7 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials_p.h +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials_p.h @@ -52,10 +52,14 @@ // #include "qsgd3d12material_p.h" +#include "qsgd3d12glyphcache_p.h" #include <private/qsgtexture_p.h> +#include <QRawFont> QT_BEGIN_NAMESPACE +class QSGD3D12RenderContext; + class QSGD3D12VertexColorMaterial : public QSGD3D12Material { public: @@ -165,6 +169,39 @@ private: QSGTexture::WrapMode m_vertical_wrap = QSGTexture::ClampToEdge; }; +class QSGD3D12TextMaterial : public QSGD3D12Material +{ +public: + QSGD3D12TextMaterial(QSGD3D12RenderContext *rc, const QRawFont &font, + QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None); + + QSGMaterialType *type() const override; + int compare(const QSGMaterial *other) const override; + + virtual int constantBufferSize() const override; + void preparePipeline(QSGD3D12ShaderState *shaders) override; + UpdateResults updatePipeline(const RenderState &state, + QSGD3D12ShaderState *shaders, + quint8 *constantBuffer) override; + + void setColor(const QColor &c) { m_color = QVector4D(c.redF(), c.greenF(), c.blueF(), c.alphaF()); } + void setColor(const QVector4D &color) { m_color = color; } + const QVector4D &color() const { return m_color; } + + void populate(const QPointF &position, + const QVector<quint32> &glyphIndexes, const QVector<QPointF> &glyphPositions, + QSGGeometry *geometry, QRectF *boundingRect, QPointF *baseLine, + const QMargins &margins = QMargins(0, 0, 0, 0)); + + QSGD3D12GlyphCache *glyphCache() const { return static_cast<QSGD3D12GlyphCache *>(m_glyphCache.data()); } + +private: + static QSGMaterialType mtype; + QVector4D m_color; + QRawFont m_font; + QExplicitlySharedDataPointer<QFontEngineGlyphCache> m_glyphCache; +}; + QT_END_NAMESPACE #endif // QSGD3D12BUILTINMATERIALS_P_H diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp index 727109cc9c..f54be683f5 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp @@ -41,6 +41,7 @@ #include "qsgd3d12rendercontext_p.h" #include "qsgd3d12rectanglenode_p.h" #include "qsgd3d12imagenode_p.h" +#include "qsgd3d12glyphnode_p.h" QT_BEGIN_NAMESPACE @@ -68,10 +69,10 @@ QSGPainterNode *QSGD3D12Context::createPainterNode(QQuickPaintedItem *item) QSGGlyphNode *QSGD3D12Context::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) { - Q_UNUSED(rc); Q_UNUSED(preferNativeGlyphNode); - Q_UNREACHABLE(); - return nullptr; + // ### distance field text rendering is not supported atm + + return new QSGD3D12GlyphNode(static_cast<QSGD3D12RenderContext *>(rc)); } QSGNinePatchNode *QSGD3D12Context::createNinePatchNode() diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp index bb1071cc28..bb990b08e0 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp @@ -252,14 +252,28 @@ void QSGD3D12DeviceManager::ensureCreated() if (warp) { qDebug("Using WARP"); - ComPtr<IDXGIAdapter> warpAdapter; - m_factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)); - HRESULT hr = D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)); + m_factory->EnumWarpAdapter(IID_PPV_ARGS(&adapter)); + HRESULT hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)); if (FAILED(hr)) { qWarning("Failed to create WARP device: 0x%x", hr); return; } } + + ComPtr<IDXGIAdapter3> adapter3; + if (SUCCEEDED(adapter.As(&adapter3))) { + DXGI_QUERY_VIDEO_MEMORY_INFO vidMemInfo; + if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &vidMemInfo))) { + qDebug("Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", + vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, + vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); + } + if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &vidMemInfo))) { + qDebug("Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", + vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, + vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); + } + } } void QSGD3D12DeviceManager::registerDeviceLossObserver(DeviceLossObserver *observer) @@ -302,6 +316,11 @@ void QSGD3D12Engine::resize() d->resize(); } +QWindow *QSGD3D12Engine::window() const +{ + return d->currentWindow(); +} + void QSGD3D12Engine::beginFrame() { d->beginFrame(); @@ -399,9 +418,19 @@ uint QSGD3D12Engine::genTexture() return d->genTexture(); } -void QSGD3D12Engine::createTextureAsync(uint id, const QImage &image, TextureCreateFlags flags) +void QSGD3D12Engine::createTexture(uint id, const QSize &size, QImage::Format format, TextureCreateFlags flags) +{ + d->createTexture(id, size, format, flags); +} + +void QSGD3D12Engine::queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos) { - return d->createTextureAsync(id, image, flags); + d->queueTextureUpload(id, QVector<QImage>() << image, QVector<QPoint>() << dstPos); +} + +void QSGD3D12Engine::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos) +{ + d->queueTextureUpload(id, images, dstPos); } void QSGD3D12Engine::releaseTexture(uint id) @@ -1028,7 +1057,8 @@ void QSGD3D12EnginePrivate::beginFrame() Texture &t(textures[idx]); if (t.fenceValue) { // may have been cleared by the previous frame t.fenceValue = 0; - t.stagingBuffer = nullptr; + t.stagingBuffers.clear(); + t.stagingHeap = nullptr; if (Q_UNLIKELY(debug_render())) qDebug("Cleaned staging data for texture %u", id); } @@ -1661,23 +1691,64 @@ uint QSGD3D12EnginePrivate::genTexture() Texture &t(textures[id - 1]); t.entryInUse = true; t.fenceValue = 0; - t.mipmap = t.waitAdded = false; return id; } -void QSGD3D12EnginePrivate::createTextureAsync(uint id, const QImage &image, QSGD3D12Engine::TextureCreateFlags flags) +static inline DXGI_FORMAT textureFormat(QImage::Format format, bool wantsAlpha, bool mipmap, + QImage::Format *imageFormat, int *bytesPerPixel) +{ + DXGI_FORMAT f = DXGI_FORMAT_R8G8B8A8_UNORM; + QImage::Format convFormat = format; + int bpp = 4; + + if (!mipmap) { + switch (format) { + case QImage::Format_Indexed8: + f = DXGI_FORMAT_A8_UNORM; + bpp = 1; + break; + case QImage::Format_RGB32: + f = DXGI_FORMAT_B8G8R8A8_UNORM; + break; + case QImage::Format_ARGB32: + f = DXGI_FORMAT_B8G8R8A8_UNORM; + convFormat = wantsAlpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + break; + case QImage::Format_ARGB32_Premultiplied: + f = DXGI_FORMAT_B8G8R8A8_UNORM; + convFormat = wantsAlpha ? format : QImage::Format_RGB32; + break; + default: + convFormat = wantsAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888; + break; + } + } else { + // Mipmap generation needs unordered access and BGRA is not an option for that. Stick to RGBA. + convFormat = wantsAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888; + } + + if (imageFormat) + *imageFormat = convFormat; + + if (bytesPerPixel) + *bytesPerPixel = bpp; + + return f; +} + +void QSGD3D12EnginePrivate::createTexture(uint id, const QSize &size, QImage::Format format, + QSGD3D12Engine::TextureCreateFlags flags) { Q_ASSERT(id); const int idx = id - 1; Q_ASSERT(idx < textures.count() && textures[idx].entryInUse); Texture &t(textures[idx]); - const bool alpha = flags & QSGD3D12Engine::CreateWithAlpha; + t.alpha = flags & QSGD3D12Engine::CreateWithAlpha; t.mipmap = flags & QSGD3D12Engine::CreateWithMipMaps; - t.waitAdded = false; - const QSize adjustedSize = !t.mipmap ? image.size() : QSGD3D12Engine::mipMapAdjustedSourceSize(image.size()); + const QSize adjustedSize = !t.mipmap ? size : QSGD3D12Engine::mipMapAdjustedSourceSize(size); D3D12_HEAP_PROPERTIES defaultHeapProp = {}; defaultHeapProp.Type = D3D12_HEAP_TYPE_DEFAULT; @@ -1688,7 +1759,7 @@ void QSGD3D12EnginePrivate::createTextureAsync(uint id, const QImage &image, QSG textureDesc.Height = adjustedSize.height(); textureDesc.DepthOrArraySize = 1; textureDesc.MipLevels = !t.mipmap ? 1 : QSGD3D12Engine::mipMapLevels(adjustedSize); - textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + textureDesc.Format = textureFormat(format, t.alpha, t.mipmap, nullptr, nullptr); textureDesc.SampleDesc.Count = 1; textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; if (t.mipmap) @@ -1726,63 +1797,140 @@ void QSGD3D12EnginePrivate::createTextureAsync(uint id, const QImage &image, QSG } if (Q_UNLIKELY(debug_render())) - qDebug("created texture %d, size %dx%d, miplevels %d", id, adjustedSize.width(), adjustedSize.height(), textureDesc.MipLevels); + qDebug("created texture %u, size %dx%d, miplevels %d", id, adjustedSize.width(), adjustedSize.height(), textureDesc.MipLevels); +} + +void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos) +{ + Q_ASSERT(id); + Q_ASSERT(images.count() == dstPos.count()); + if (images.isEmpty()) + return; + + const int idx = id - 1; + Q_ASSERT(idx < textures.count() && textures[idx].entryInUse); + Texture &t(textures[idx]); + Q_ASSERT(t.texture); + + // When mipmapping is not in use, image can be smaller than the size passed + // to createTexture() and dstPos can specify a non-zero destination position. + + if (t.mipmap && (images.count() != 1 || dstPos.count() != 1 || !dstPos[0].isNull())) { + qWarning("Mipmapped textures (%u) do not support partial uploads", id); + return; + } + + // This function cannot be called until the previous upload has finished, + // but one call can initiate the upload of multiple sub-regions which still + // counts as one upload from the viewpoint of the rest of the engine (and + // only the completion of the whole set can be waited on). + + if (t.fenceValue) { + qWarning("Attempted to queue texture upload (%u) while a previous upload is still in progress", id); + return; + } t.fenceValue = nextTextureUploadFenceValue.fetchAndAddAcquire(1) + 1; + t.waitAdded = false; - UINT64 bufferSize; - D3D12_PLACED_SUBRESOURCE_FOOTPRINT textureLayout; - device->GetCopyableFootprints(&textureDesc, 0, 1, 0, &textureLayout, nullptr, nullptr, &bufferSize); + if (Q_UNLIKELY(debug_render())) + qDebug("adding upload for texture %u on the copy queue", id); - D3D12_RESOURCE_DESC bufDesc = {}; - bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - bufDesc.Width = bufferSize; - bufDesc.Height = 1; - bufDesc.DepthOrArraySize = 1; - bufDesc.MipLevels = 1; - bufDesc.Format = DXGI_FORMAT_UNKNOWN; - bufDesc.SampleDesc.Count = 1; - bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + D3D12_RESOURCE_DESC textureDesc = t.texture->GetDesc(); + const QSize adjustedTextureSize(textureDesc.Width, textureDesc.Height); + int totalSize = 0; + for (const QImage &image : images) { + int bytesPerPixel; + textureFormat(image.format(), t.alpha, t.mipmap, nullptr, &bytesPerPixel); + const int w = !t.mipmap ? image.width() : adjustedTextureSize.width(); + const int h = !t.mipmap ? image.height() : adjustedTextureSize.height(); + const int stride = alignedSize(w * bytesPerPixel, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); + totalSize += alignedSize(h * stride, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); + } + + if (Q_UNLIKELY(debug_render())) + qDebug("%d sub-uploads, heap size %d bytes", images.count(), totalSize); + + // Instead of individual committed resources for each upload buffer, + // allocate only once and use placed resources. D3D12_HEAP_PROPERTIES uploadHeapProp = {}; uploadHeapProp.Type = D3D12_HEAP_TYPE_UPLOAD; + D3D12_HEAP_DESC uploadHeapDesc = {}; + uploadHeapDesc.SizeInBytes = totalSize; + uploadHeapDesc.Properties = uploadHeapProp; + uploadHeapDesc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS; - if (FAILED(device->CreateCommittedResource(&uploadHeapProp, D3D12_HEAP_FLAG_NONE, &bufDesc, - D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&t.stagingBuffer)))) { - qWarning("Failed to create texture upload buffer"); + if (FAILED(device->CreateHeap(&uploadHeapDesc, IID_PPV_ARGS(&t.stagingHeap)))) { + qWarning("Failed to create texture upload heap of size %d", totalSize); return; } - // ### conversion and scaling are slow. the latter goes away once npot - // mipmap generation is supported. figure out something for the former. - QImage convImage = image.convertToFormat(alpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888); - if (t.mipmap && adjustedSize != convImage.size()) - convImage = convImage.scaled(adjustedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + int placedOffset = 0; + for (int i = 0; i < images.count(); ++i) { + QImage::Format convFormat; + int bytesPerPixel; + textureFormat(images[i].format(), t.alpha, t.mipmap, &convFormat, &bytesPerPixel); - quint8 *p = nullptr; - const D3D12_RANGE readRange = { 0, 0 }; - if (FAILED(t.stagingBuffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) { - qWarning("Map failed (texture upload buffer)"); - return; - } - quint8 *lp = p + textureLayout.Offset; - for (uint y = 0; y < textureDesc.Height; ++y) { - memcpy(lp, convImage.scanLine(y), convImage.width() * 4); - lp += textureLayout.Footprint.RowPitch; - } - t.stagingBuffer->Unmap(0, nullptr); + QImage convImage = images[i].format() == convFormat ? images[i] : images[i].convertToFormat(convFormat); + + if (t.mipmap && adjustedTextureSize != convImage.size()) + convImage = convImage.scaled(adjustedTextureSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + + const int stride = alignedSize(convImage.width() * bytesPerPixel, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); + + D3D12_RESOURCE_DESC bufDesc = {}; + bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + bufDesc.Width = stride * convImage.height(); + bufDesc.Height = 1; + bufDesc.DepthOrArraySize = 1; + bufDesc.MipLevels = 1; + bufDesc.Format = DXGI_FORMAT_UNKNOWN; + bufDesc.SampleDesc.Count = 1; + bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + + Texture::StagingEntry sdata; + if (FAILED(device->CreatePlacedResource(t.stagingHeap.Get(), placedOffset, + &bufDesc, D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, IID_PPV_ARGS(&sdata.buffer)))) { + qWarning("Failed to create texture upload buffer"); + return; + } + + quint8 *p = nullptr; + const D3D12_RANGE readRange = { 0, 0 }; + if (FAILED(sdata.buffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) { + qWarning("Map failed (texture upload buffer)"); + return; + } + for (int y = 0, ye = convImage.height(); y < ye; ++y) { + memcpy(p, convImage.scanLine(y), convImage.width() * 4); + p += stride; + } + sdata.buffer->Unmap(0, nullptr); + + copyCommandList->Reset(copyCommandAllocator.Get(), nullptr); + + D3D12_TEXTURE_COPY_LOCATION dstLoc; + dstLoc.pResource = t.texture.Get(); + dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dstLoc.SubresourceIndex = 0; - copyCommandList->Reset(copyCommandAllocator.Get(), nullptr); + D3D12_TEXTURE_COPY_LOCATION srcLoc; + srcLoc.pResource = sdata.buffer.Get(); + srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + srcLoc.PlacedFootprint.Offset = 0; + srcLoc.PlacedFootprint.Footprint.Format = textureDesc.Format; + srcLoc.PlacedFootprint.Footprint.Width = convImage.width(); + srcLoc.PlacedFootprint.Footprint.Height = convImage.height(); + srcLoc.PlacedFootprint.Footprint.Depth = 1; + srcLoc.PlacedFootprint.Footprint.RowPitch = stride; - D3D12_TEXTURE_COPY_LOCATION dstLoc; - dstLoc.pResource = t.texture.Get(); - dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dstLoc.SubresourceIndex = 0; - D3D12_TEXTURE_COPY_LOCATION srcLoc; - srcLoc.pResource = t.stagingBuffer.Get(); - srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - srcLoc.PlacedFootprint = textureLayout; - copyCommandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr); + copyCommandList->CopyTextureRegion(&dstLoc, dstPos[i].x(), dstPos[i].y(), 0, &srcLoc, nullptr); + + t.stagingBuffers.append(sdata); + placedOffset += alignedSize(bufDesc.Width, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); + } copyCommandList->Close(); ID3D12CommandList *commandLists[] = { copyCommandList.Get() }; diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h index 49df43f3c0..0c6772e9ed 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h @@ -260,6 +260,7 @@ public: bool attachToWindow(QWindow *window); void releaseResources(); void resize(); + QWindow *window() const; void beginFrame(); void endFrame(); @@ -306,7 +307,9 @@ public: Q_DECLARE_FLAGS(TextureCreateFlags, TextureCreateFlag) uint genTexture(); - void createTextureAsync(uint id, const QImage &image, TextureCreateFlags flags); + void createTexture(uint id, const QSize &size, QImage::Format format, TextureCreateFlags flags); + void queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos = QPoint()); + void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos); void releaseTexture(uint id); SIZE_T textureSRV(uint id) const; void activateTexture(uint id); diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h index db73e0329d..1788a7b2c2 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h @@ -134,6 +134,7 @@ public: bool isInitialized() const { return initialized; } void releaseResources(); void resize(); + QWindow *currentWindow() const { return window; } void beginFrame(); void endFrame(); @@ -163,7 +164,8 @@ public: void waitGPU(); uint genTexture(); - void createTextureAsync(uint id, const QImage &image, QSGD3D12Engine::TextureCreateFlags flags); + void createTexture(uint id, const QSize &size, QImage::Format format, QSGD3D12Engine::TextureCreateFlags flags); + void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos); void releaseTexture(uint id); SIZE_T textureSRV(uint id) const; void activateTexture(uint id); @@ -282,8 +284,13 @@ private: D3D12_CPU_DESCRIPTOR_HANDLE srv; quint64 fenceValue = 0; bool waitAdded = false; - ComPtr<ID3D12Resource> stagingBuffer; + ComPtr<ID3D12Heap> stagingHeap; + struct StagingEntry { + ComPtr<ID3D12Resource> buffer; + }; + QVector<StagingEntry> stagingBuffers; QVector<D3D12_CPU_DESCRIPTOR_HANDLE> mipUAVs; + bool alpha = true; bool mipmap = false; }; diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache.cpp new file mode 100644 index 0000000000..b1c7c06054 --- /dev/null +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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 "qsgd3d12glyphcache_p.h" +#include "qsgd3d12engine_p.h" + +QT_BEGIN_NAMESPACE + +// Keep it simple: allocate a large texture and never resize. +static const int TEXTURE_WIDTH = 2048; +static const int TEXTURE_HEIGHT = 2048; + +QSGD3D12GlyphCache::QSGD3D12GlyphCache(QSGD3D12Engine *engine, QFontEngine::GlyphFormat format, const QTransform &matrix) + : QTextureGlyphCache(format, matrix), + m_engine(engine) +{ +} + +QSGD3D12GlyphCache::~QSGD3D12GlyphCache() +{ + if (m_id) + m_engine->releaseTexture(m_id); +} + +void QSGD3D12GlyphCache::createTextureData(int, int) +{ + qDebug("create"); + m_id = m_engine->genTexture(); + Q_ASSERT(m_id); + m_engine->createTexture(m_id, QSize(TEXTURE_WIDTH, TEXTURE_HEIGHT), + QImage::Format_ARGB32_Premultiplied, QSGD3D12Engine::CreateWithAlpha); +} + +void QSGD3D12GlyphCache::resizeTextureData(int, int) +{ + // nothing to do here +} + +void QSGD3D12GlyphCache::beginFillTexture() +{ + qDebug("begin"); +} + +void QSGD3D12GlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) +{ + qDebug("fill %x", glyph); +} + +void QSGD3D12GlyphCache::endFillTexture() +{ + qDebug("end"); +} + +int QSGD3D12GlyphCache::glyphPadding() const +{ + return 1; +} + +int QSGD3D12GlyphCache::maxTextureWidth() const +{ + return TEXTURE_WIDTH; +} + +int QSGD3D12GlyphCache::maxTextureHeight() const +{ + return TEXTURE_HEIGHT; +} + +void QSGD3D12GlyphCache::activateTexture() +{ + if (m_id) + m_engine->activateTexture(m_id); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache_p.h new file mode 100644 index 0000000000..a92558171b --- /dev/null +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSGD3D12GLYPHCACHE_P_H +#define QSGD3D12GLYPHCACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qtextureglyphcache_p.h> + +QT_BEGIN_NAMESPACE + +class QSGD3D12Engine; + +class QSGD3D12GlyphCache : public QTextureGlyphCache +{ +public: + QSGD3D12GlyphCache(QSGD3D12Engine *engine, QFontEngine::GlyphFormat format, const QTransform &matrix); + ~QSGD3D12GlyphCache(); + + void createTextureData(int width, int height) override; + void resizeTextureData(int width, int height) override; + void beginFillTexture() override; + void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) override; + void endFillTexture() override; + int glyphPadding() const override; + int maxTextureWidth() const override; + int maxTextureHeight() const override; + + void activateTexture(); + +private: + QSGD3D12Engine *m_engine; + uint m_id = 0; +}; + +QT_END_NAMESPACE + +#endif // QSGD3D12GLYPHCACHE_P_H diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode.cpp new file mode 100644 index 0000000000..c5e53219fa --- /dev/null +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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 "qsgd3d12glyphnode_p.h" +#include "qsgd3d12builtinmaterials_p.h" + +QT_BEGIN_NAMESPACE + +void QSGD3D12GlyphNode::setMaterialColor(const QColor &color) +{ + static_cast<QSGD3D12TextMaterial *>(m_material)->setColor(color); +} + +void QSGD3D12GlyphNode::update() +{ + QRawFont font = m_glyphs.rawFont(); + QMargins margins(0, 0, 0, 0); + + if (m_style == QQuickText::Normal) { + // QSGBasicGlyphNode dtor will delete + m_material = new QSGD3D12TextMaterial(m_rc, font); + } else if (m_style == QQuickText::Outline) { + // ### not yet supported +// QSGOutlinedTextMaterial *material = new QSGOutlinedTextMaterial(font); +// material->setStyleColor(m_styleColor); +// m_material = material; + margins = QMargins(1, 1, 1, 1); + } else { + // ### not yet supported +// QSGStyledTextMaterial *material = new QSGStyledTextMaterial(font); + if (m_style == QQuickText::Sunken) { +// material->setStyleShift(QVector2D(0, -1)); + margins.setTop(1); + } else if (m_style == QQuickText::Raised) { +// material->setStyleShift(QVector2D(0, 1)); + margins.setBottom(1); + } +// material->setStyleColor(m_styleColor); +// m_material = material; + } + + QSGD3D12TextMaterial *textMaterial = static_cast<QSGD3D12TextMaterial *>(m_material); + textMaterial->setColor(m_color); + + QRectF boundingRect; + textMaterial->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(), + &boundingRect, &m_baseLine, margins); + setBoundingRect(boundingRect); + + setMaterial(m_material); + markDirty(DirtyGeometry); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode_p.h new file mode 100644 index 0000000000..d04a8e8777 --- /dev/null +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSGD3D12GLYPHNODE_P_H +#define QSGD3D12GLYPHNODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgbasicglyphnode_p.h> + +QT_BEGIN_NAMESPACE + +class QSGD3D12RenderContext; + +class QSGD3D12GlyphNode : public QSGBasicGlyphNode +{ +public: + QSGD3D12GlyphNode(QSGD3D12RenderContext *rc) : m_rc(rc) { } + + void setMaterialColor(const QColor &color) override; + void update() override; + +private: + QSGD3D12RenderContext *m_rc; +}; + +QT_END_NAMESPACE + +#endif // QSGD3D12GLYPHNODE_P_H diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h index 7f47f7daa4..ef4b38884a 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h @@ -51,12 +51,12 @@ // We mean it. // -#include <private/qsgdefaultimagenode_p.h> +#include <private/qsgbasicimagenode_p.h> #include "qsgd3d12builtinmaterials_p.h" QT_BEGIN_NAMESPACE -class QSGD3D12ImageNode : public QSGDefaultNoMaterialImageNode +class QSGD3D12ImageNode : public QSGBasicImageNode { public: QSGD3D12ImageNode(); diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h index 95df900489..95f734bc4c 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h @@ -51,12 +51,12 @@ // We mean it. // -#include <private/qsgdefaultrectanglenode_p.h> +#include <private/qsgbasicrectanglenode_p.h> #include "qsgd3d12builtinmaterials_p.h" QT_BEGIN_NAMESPACE -class QSGD3D12RectangleNode : public QSGDefaultNoMaterialRectangleNode +class QSGD3D12RectangleNode : public QSGBasicRectangleNode { public: QSGD3D12RectangleNode(); diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp index 306a54901a..e2b91619cc 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp @@ -43,6 +43,14 @@ QT_BEGIN_NAMESPACE +// NOTE: Avoid categorized logging. It is slow. + +#define DECLARE_DEBUG_VAR(variable) \ + static bool debug_ ## variable() \ + { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; } + +DECLARE_DEBUG_VAR(render) + QSGD3D12RenderContext::QSGD3D12RenderContext(QSGContext *ctx) : QSGRenderContext(ctx) { @@ -53,6 +61,30 @@ void QSGD3D12RenderContext::initialize(QOpenGLContext *) Q_UNREACHABLE(); } +void QSGD3D12RenderContext::invalidate() +{ + if (Q_UNLIKELY(debug_render())) + qDebug("rendercontext invalidate engine %p, %d/%d/%d", m_engine, + m_texturesToDelete.count(), m_textures.count(), m_fontEnginesToClean.count()); + + qDeleteAll(m_texturesToDelete); + m_texturesToDelete.clear(); + + qDeleteAll(m_textures); + m_textures.clear(); + + for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(), + end = m_fontEnginesToClean.constEnd(); it != end; ++it) { + (*it)->clearGlyphCache(m_engine); + if (!(*it)->ref.deref()) + delete *it; + } + m_fontEnginesToClean.clear(); + + m_sg->renderContextInvalidated(this); + emit invalidated(); +} + QSGTexture *QSGD3D12RenderContext::createTexture(const QImage &image, uint flags) const { Q_ASSERT(m_engine); @@ -61,7 +93,7 @@ QSGTexture *QSGD3D12RenderContext::createTexture(const QImage &image, uint flags return t; } -QSGRenderer *QSGD3D12RenderContext::createRenderer() + QSGRenderer *QSGD3D12RenderContext::createRenderer() { return new QSGD3D12Renderer(this); } diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h index ec3bf81288..2e05632ad9 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h @@ -63,6 +63,7 @@ public: QSGD3D12RenderContext(QSGContext *ctx); void initialize(QOpenGLContext *) override; // not in use + void invalidate() override; void renderNextFrame(QSGRenderer *renderer, GLuint fbo) override; QSGTexture *createTexture(const QImage &image, uint flags) const override; QSGRenderer *createRenderer() override; diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp index 504e87b2f5..aace6cba28 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp @@ -61,6 +61,9 @@ QSGD3D12RenderLoop::QSGD3D12RenderLoop() qDebug("new d3d12 render loop"); sg = new QSGD3D12Context; + + // One global rendercontext, it will be the renderloop's responsibility to + // set the correct window-specific engine via setEngine() later on. rc = new QSGD3D12RenderContext(sg); } @@ -112,7 +115,7 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) qDebug() << "window destroyed" << window; WindowData &data(m_windows[window]); - delete data.engine; + QSGD3D12Engine *engine = data.engine; m_windows.remove(window); hide(window); @@ -121,9 +124,12 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) wd->cleanupNodesOnShutdown(); if (m_windows.isEmpty()) { + rc->setEngine(engine); rc->invalidate(); QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); } + + delete engine; } void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window) @@ -288,8 +294,6 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) lastFrameTime = QTime::currentTime(); } - rc->setEngine(nullptr); - // Might have been set during syncSceneGraph() if (data.updatePending) maybeUpdate(window); diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp index c76e730a6a..c1164b5add 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp @@ -125,7 +125,8 @@ void QSGD3D12Texture::bind() if (m_createdWithMipMaps) createFlags |= QSGD3D12Engine::CreateWithMipMaps; - m_engine->createTextureAsync(m_id, m_image, createFlags); + m_engine->createTexture(m_id, m_image.size(), m_image.format(), createFlags); + m_engine->queueTextureUpload(m_id, m_image); } // Here we know that the texture is going to be used in the current frame diff --git a/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri b/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri index 96b0c94565..5999fadda6 100644 --- a/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri +++ b/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri @@ -54,12 +54,23 @@ mipmapgen_cshader.header = cs_mipmapgen.hlslh mipmapgen_cshader.entry = CS_Generate4MipMaps mipmapgen_cshader.type = cs_5_0 +textmask_VSPS = $$PWD/textmask.hlsl +textmask_vshader.input = textmask_VSPS +textmask_vshader.header = vs_textmask.hlslh +textmask_vshader.entry = VS_TextMask +textmask_vshader.type = vs_5_0 +textmask_pshader24.input = textmask_VSPS +textmask_pshader24.header = ps_textmask24.hlslh +textmask_pshader24.entry = PS_TextMask24 +textmask_pshader24.type = ps_5_0 + HLSL_SHADERS = \ vertexcolor_vshader vertexcolor_pshader \ stencilclip_vshader stencilclip_pshader \ smoothcolor_vshader smoothcolor_pshader \ texture_vshader texture_pshader \ smoothtexture_vshader smoothtexture_pshader \ - mipmapgen_cshader + mipmapgen_cshader \ + textmask_vshader textmask_pshader24 load(hlsl_bytecode_header) diff --git a/src/quick/scenegraph/adaptations/d3d12/shaders/textmask.hlsl b/src/quick/scenegraph/adaptations/d3d12/shaders/textmask.hlsl new file mode 100644 index 0000000000..b7963d92fe --- /dev/null +++ b/src/quick/scenegraph/adaptations/d3d12/shaders/textmask.hlsl @@ -0,0 +1,36 @@ +struct VSInput +{ + float4 position : POSITION; + float2 coord : TEXCOORD0; +}; + +cbuffer ConstantBuffer : register(b0) +{ + float4x4 mvp; + float2 textureScale; + float dpr; + float color; +}; + +struct PSInput +{ + float4 position : SV_POSITION; + float2 coord : TEXCOORD0; +}; + +Texture2D tex : register(t0); +SamplerState samp : register(s0); + +PSInput VS_TextMask(VSInput input) +{ + PSInput result; + result.position = mul(mvp, floor(input.position * dpr + 0.5) / dpr); + result.coord = input.coord * textureScale; + return result; +} + +float4 PS_TextMask24(PSInput input) : SV_TARGET +{ + float4 glyph = tex.Sample(samp, input.coord); + return float4(glyph.rgb * color, glyph.a); +} diff --git a/src/quick/scenegraph/qsgbasicglyphnode.cpp b/src/quick/scenegraph/qsgbasicglyphnode.cpp new file mode 100644 index 0000000000..cecfcbfdf9 --- /dev/null +++ b/src/quick/scenegraph/qsgbasicglyphnode.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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 "qsgbasicglyphnode_p.h" +#include <qsgmaterial.h> // just so that we can safely do delete m_material in the dtor + +QT_BEGIN_NAMESPACE + +QSGBasicGlyphNode::QSGBasicGlyphNode() + : m_style(QQuickText::Normal) + , m_material(0) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) +{ + m_geometry.setDrawingMode(GL_TRIANGLES); + setGeometry(&m_geometry); +} + +QSGBasicGlyphNode::~QSGBasicGlyphNode() +{ + delete m_material; +} + +void QSGBasicGlyphNode::setColor(const QColor &color) +{ + m_color = color; + if (m_material != 0) { + setMaterialColor(color); + markDirty(DirtyMaterial); + } +} + +void QSGBasicGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) +{ + if (m_material != 0) + delete m_material; + + m_position = position; + m_glyphs = glyphs; + +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("glyphs")); +#endif +} + +void QSGBasicGlyphNode::setStyle(QQuickText::TextStyle style) +{ + if (m_style == style) + return; + m_style = style; +} + +void QSGBasicGlyphNode::setStyleColor(const QColor &color) +{ + if (m_styleColor == color) + return; + m_styleColor = color; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgbasicglyphnode_p.h b/src/quick/scenegraph/qsgbasicglyphnode_p.h new file mode 100644 index 0000000000..6c6e1d7cc6 --- /dev/null +++ b/src/quick/scenegraph/qsgbasicglyphnode_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSGBASICGLYPHNODE_P_H +#define QSGBASICGLYPHNODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class QSGMaterial; + +class QSGBasicGlyphNode: public QSGGlyphNode +{ +public: + QSGBasicGlyphNode(); + virtual ~QSGBasicGlyphNode(); + + virtual QPointF baseLine() const { return m_baseLine; } + virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs); + virtual void setColor(const QColor &color); + + virtual void setPreferredAntialiasingMode(AntialiasingMode) { } + virtual void setStyle(QQuickText::TextStyle); + virtual void setStyleColor(const QColor &); + + virtual void setMaterialColor(const QColor &color) = 0; + virtual void update() = 0; + +protected: + QGlyphRun m_glyphs; + QPointF m_position; + QColor m_color; + QQuickText::TextStyle m_style; + QColor m_styleColor; + + QPointF m_baseLine; + QSGMaterial *m_material; + + QSGGeometry m_geometry; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/qsgbasicimagenode.cpp b/src/quick/scenegraph/qsgbasicimagenode.cpp new file mode 100644 index 0000000000..a76c788656 --- /dev/null +++ b/src/quick/scenegraph/qsgbasicimagenode.cpp @@ -0,0 +1,519 @@ +/**************************************************************************** +** +** 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 "qsgbasicimagenode_p.h" + +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +namespace +{ + struct SmoothVertex + { + float x, y, u, v; + float dx, dy, du, dv; + }; + + const QSGGeometry::AttributeSet &smoothAttributeSet() + { + static QSGGeometry::Attribute data[] = { + QSGGeometry::Attribute::createWithSemantic(0, 2, GL_FLOAT, QSGGeometry::Attribute::POSITION), + QSGGeometry::Attribute::createWithSemantic(1, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD), + QSGGeometry::Attribute::createWithSemantic(2, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD1), + QSGGeometry::Attribute::createWithSemantic(3, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD2) + }; + static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data }; + return attrs; + } +} + +QSGBasicImageNode::QSGBasicImageNode() + : m_innerSourceRect(0, 0, 1, 1) + , m_subSourceRect(0, 0, 1, 1) + , m_antialiasing(false) + , m_mirror(false) + , m_dirtyGeometry(false) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) +{ + setGeometry(&m_geometry); + +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("image")); +#endif +} + +void QSGBasicImageNode::setTargetRect(const QRectF &rect) +{ + if (rect == m_targetRect) + return; + m_targetRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setInnerTargetRect(const QRectF &rect) +{ + if (rect == m_innerTargetRect) + return; + m_innerTargetRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setInnerSourceRect(const QRectF &rect) +{ + if (rect == m_innerSourceRect) + return; + m_innerSourceRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setSubSourceRect(const QRectF &rect) +{ + if (rect == m_subSourceRect) + return; + m_subSourceRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setTexture(QSGTexture *texture) +{ + Q_ASSERT(texture); + + setMaterialTexture(texture); + updateMaterialBlending(); + + markDirty(DirtyMaterial); + + // Because the texture can be a different part of the atlas, we need to update it... + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setAntialiasing(bool antialiasing) +{ + if (antialiasing == m_antialiasing) + return; + m_antialiasing = antialiasing; + if (m_antialiasing) { + setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); + setFlag(OwnsGeometry, true); + } else { + setGeometry(&m_geometry); + setFlag(OwnsGeometry, false); + } + updateMaterialAntialiasing(); + m_dirtyGeometry = true; +} + +void QSGBasicImageNode::setMirror(bool mirror) +{ + if (mirror == m_mirror) + return; + m_mirror = mirror; + m_dirtyGeometry = true; +} + + +void QSGBasicImageNode::update() +{ + if (m_dirtyGeometry) + updateGeometry(); +} + +void QSGBasicImageNode::preprocess() +{ + bool doDirty = false; + QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(materialTexture()); + if (t) { + doDirty = t->updateTexture(); + if (doDirty) + updateGeometry(); + } + + if (updateMaterialBlending()) + doDirty = true; + + if (doDirty) + markDirty(DirtyMaterial); +} + +namespace { + struct X { float x, tx; }; + struct Y { float y, ty; }; +} + +static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight, + quint16 bottomLeft, quint16 bottomRight) +{ + *(*indices)++ = topLeft; + *(*indices)++ = bottomLeft; + *(*indices)++ = bottomRight; + *(*indices)++ = bottomRight; + *(*indices)++ = topRight; + *(*indices)++ = topLeft; +} + +void QSGBasicImageNode::updateGeometry() +{ + Q_ASSERT(!m_targetRect.isEmpty()); + const QSGTexture *t = materialTexture(); + if (!t) { + QSGGeometry *g = geometry(); + g->allocate(4); + g->setDrawingMode(GL_TRIANGLE_STRIP); + memset(g->vertexData(), 0, g->sizeOfVertex() * 4); + } else { + QRectF sourceRect = t->normalizedTextureSubRect(); + + QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(), + sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(), + m_innerSourceRect.width() * sourceRect.width(), + m_innerSourceRect.height() * sourceRect.height()); + + bool hasMargins = m_targetRect != m_innerTargetRect; + + int floorLeft = qFloor(m_subSourceRect.left()); + int ceilRight = qCeil(m_subSourceRect.right()); + int floorTop = qFloor(m_subSourceRect.top()); + int ceilBottom = qCeil(m_subSourceRect.bottom()); + int hTiles = ceilRight - floorLeft; + int vTiles = ceilBottom - floorTop; + + bool hasTiles = hTiles != 1 || vTiles != 1; + bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1); + + // An image can be rendered as a single quad if: + // - There are no margins, and either: + // - the image isn't repeated + // - the source rectangle fills the entire texture so that texture wrapping can be used, + // and NPOT is supported + if (!hasMargins && (!hasTiles || (fullTexture && supportsWrap(t->textureSize())))) { + QRectF sr; + if (!fullTexture) { + sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(), + innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(), + m_subSourceRect.width() * innerSourceRect.width(), + m_subSourceRect.height() * innerSourceRect.height()); + } else { + sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop, + m_subSourceRect.width(), m_subSourceRect.height()); + } + if (m_mirror) { + qreal oldLeft = sr.left(); + sr.setLeft(sr.right()); + sr.setRight(oldLeft); + } + + if (m_antialiasing) { + QSGGeometry *g = geometry(); + Q_ASSERT(g != &m_geometry); + g->allocate(8, 14); + g->setDrawingMode(GL_TRIANGLE_STRIP); + SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); + float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) + ? m_targetRect.width() : m_targetRect.height()) * 0.5f; + float sx = float(sr.width() / m_targetRect.width()); + float sy = float(sr.height() / m_targetRect.height()); + for (int d = -1; d <= 1; d += 2) { + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 2; ++i, ++vertices) { + vertices->x = m_targetRect.x() + i * m_targetRect.width(); + vertices->y = m_targetRect.y() + j * m_targetRect.height(); + vertices->u = sr.x() + i * sr.width(); + vertices->v = sr.y() + j * sr.height(); + vertices->dx = (i == 0 ? delta : -delta) * d; + vertices->dy = (j == 0 ? delta : -delta) * d; + vertices->du = (d < 0 ? 0 : vertices->dx * sx); + vertices->dv = (d < 0 ? 0 : vertices->dy * sy); + } + } + } + Q_ASSERT(vertices - g->vertexCount() == g->vertexData()); + static const quint16 indices[] = { + 0, 4, 1, 5, 3, 7, 2, 6, 0, 4, + 4, 6, 5, 7 + }; + Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices)); + memcpy(g->indexDataAsUShort(), indices, sizeof(indices)); + } else { + m_geometry.allocate(4); + m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); + QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); + } + } else { + int hCells = hTiles; + int vCells = vTiles; + if (m_innerTargetRect.width() == 0) + hCells = 0; + if (m_innerTargetRect.left() != m_targetRect.left()) + ++hCells; + if (m_innerTargetRect.right() != m_targetRect.right()) + ++hCells; + if (m_innerTargetRect.height() == 0) + vCells = 0; + if (m_innerTargetRect.top() != m_targetRect.top()) + ++vCells; + if (m_innerTargetRect.bottom() != m_targetRect.bottom()) + ++vCells; + QVarLengthArray<X, 32> xData(2 * hCells); + QVarLengthArray<Y, 32> yData(2 * vCells); + X *xs = xData.data(); + Y *ys = yData.data(); + + if (m_innerTargetRect.left() != m_targetRect.left()) { + xs[0].x = m_targetRect.left(); + xs[0].tx = sourceRect.left(); + xs[1].x = m_innerTargetRect.left(); + xs[1].tx = innerSourceRect.left(); + xs += 2; + } + if (m_innerTargetRect.width() != 0) { + xs[0].x = m_innerTargetRect.left(); + xs[0].tx = innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(); + ++xs; + float b = m_innerTargetRect.width() / m_subSourceRect.width(); + float a = m_innerTargetRect.x() - m_subSourceRect.x() * b; + for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { + xs[0].x = xs[1].x = a + b * i; + xs[0].tx = innerSourceRect.right(); + xs[1].tx = innerSourceRect.left(); + xs += 2; + } + xs[0].x = m_innerTargetRect.right(); + xs[0].tx = innerSourceRect.x() + (m_subSourceRect.right() - ceilRight + 1) * innerSourceRect.width(); + ++xs; + } + if (m_innerTargetRect.right() != m_targetRect.right()) { + xs[0].x = m_innerTargetRect.right(); + xs[0].tx = innerSourceRect.right(); + xs[1].x = m_targetRect.right(); + xs[1].tx = sourceRect.right(); + xs += 2; + } + Q_ASSERT(xs == xData.data() + xData.size()); + if (m_mirror) { + float leftPlusRight = m_targetRect.left() + m_targetRect.right(); + int count = xData.size(); + xs = xData.data(); + for (int i = 0; i < count >> 1; ++i) + qSwap(xs[i], xs[count - 1 - i]); + for (int i = 0; i < count; ++i) + xs[i].x = leftPlusRight - xs[i].x; + } + + if (m_innerTargetRect.top() != m_targetRect.top()) { + ys[0].y = m_targetRect.top(); + ys[0].ty = sourceRect.top(); + ys[1].y = m_innerTargetRect.top(); + ys[1].ty = innerSourceRect.top(); + ys += 2; + } + if (m_innerTargetRect.height() != 0) { + ys[0].y = m_innerTargetRect.top(); + ys[0].ty = innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(); + ++ys; + float b = m_innerTargetRect.height() / m_subSourceRect.height(); + float a = m_innerTargetRect.y() - m_subSourceRect.y() * b; + for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { + ys[0].y = ys[1].y = a + b * i; + ys[0].ty = innerSourceRect.bottom(); + ys[1].ty = innerSourceRect.top(); + ys += 2; + } + ys[0].y = m_innerTargetRect.bottom(); + ys[0].ty = innerSourceRect.y() + (m_subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height(); + ++ys; + } + if (m_innerTargetRect.bottom() != m_targetRect.bottom()) { + ys[0].y = m_innerTargetRect.bottom(); + ys[0].ty = innerSourceRect.bottom(); + ys[1].y = m_targetRect.bottom(); + ys[1].ty = sourceRect.bottom(); + ys += 2; + } + Q_ASSERT(ys == yData.data() + yData.size()); + + if (m_antialiasing) { + QSGGeometry *g = geometry(); + Q_ASSERT(g != &m_geometry); + + g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, + hCells * vCells * 6 + (hCells + vCells) * 12); + g->setDrawingMode(GL_TRIANGLES); + SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); + memset(vertices, 0, g->vertexCount() * g->sizeOfVertex()); + quint16 *indices = g->indexDataAsUShort(); + + // The deltas are how much the fuzziness can reach into the image. + // Only the border vertices are moved by the vertex shader, so the fuzziness + // can't reach further into the image than the closest interior vertices. + float leftDx = xData.at(1).x - xData.at(0).x; + float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x; + float topDy = yData.at(1).y - yData.at(0).y; + float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y; + + float leftDu = xData.at(1).tx - xData.at(0).tx; + float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx; + float topDv = yData.at(1).ty - yData.at(0).ty; + float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty; + + if (hCells == 1) { + leftDx = rightDx *= 0.5f; + leftDu = rightDu *= 0.5f; + } + if (vCells == 1) { + topDy = bottomDy *= 0.5f; + topDv = bottomDv *= 0.5f; + } + + // This delta is how much the fuzziness can reach out from the image. + float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) + ? m_targetRect.width() : m_targetRect.height()) * 0.5f; + + quint16 index = 0; + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + bool isTop = j == 0; + bool isBottom = j == vCells - 1; + for (int i = 0; i < hCells; ++i, xs += 2) { + bool isLeft = i == 0; + bool isRight = i == hCells - 1; + + SmoothVertex *v = vertices + index; + + quint16 topLeft = index; + for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) { + v->x = xs[0].x; + v->u = xs[0].tx; + v->y = ys[0].y; + v->v = ys[0].ty; + } + + quint16 topRight = index; + for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) { + v->x = xs[1].x; + v->u = xs[1].tx; + v->y = ys[0].y; + v->v = ys[0].ty; + } + + quint16 bottomLeft = index; + for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) { + v->x = xs[0].x; + v->u = xs[0].tx; + v->y = ys[1].y; + v->v = ys[1].ty; + } + + quint16 bottomRight = index; + for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) { + v->x = xs[1].x; + v->u = xs[1].tx; + v->y = ys[1].y; + v->v = ys[1].ty; + } + + appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight); + + if (isTop) { + vertices[topLeft].dy = vertices[topRight].dy = topDy; + vertices[topLeft].dv = vertices[topRight].dv = topDv; + vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta; + appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight); + } + + if (isBottom) { + vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy; + vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv; + vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta; + appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1); + } + + if (isLeft) { + vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx; + vertices[topLeft].du = vertices[bottomLeft].du = leftDu; + vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta; + appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft); + } + + if (isRight) { + vertices[topRight].dx = vertices[bottomRight].dx = -rightDx; + vertices[topRight].du = vertices[bottomRight].du = -rightDu; + vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta; + appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1); + } + } + } + + Q_ASSERT(index == g->vertexCount()); + Q_ASSERT(indices - g->indexCount() == g->indexData()); + } else { + m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6); + m_geometry.setDrawingMode(GL_TRIANGLES); + QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D(); + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + for (int i = 0; i < hCells; ++i, xs += 2) { + vertices[0].x = vertices[2].x = xs[0].x; + vertices[0].tx = vertices[2].tx = xs[0].tx; + vertices[1].x = vertices[3].x = xs[1].x; + vertices[1].tx = vertices[3].tx = xs[1].tx; + + vertices[0].y = vertices[1].y = ys[0].y; + vertices[0].ty = vertices[1].ty = ys[0].ty; + vertices[2].y = vertices[3].y = ys[1].y; + vertices[2].ty = vertices[3].ty = ys[1].ty; + + vertices += 4; + } + } + + quint16 *indices = m_geometry.indexDataAsUShort(); + for (int i = 0; i < 4 * vCells * hCells; i += 4) + appendQuad(&indices, i, i + 1, i + 2, i + 3); + } + } + } + markDirty(DirtyGeometry); + m_dirtyGeometry = false; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgbasicimagenode_p.h b/src/quick/scenegraph/qsgbasicimagenode_p.h new file mode 100644 index 0000000000..6820db7fe3 --- /dev/null +++ b/src/quick/scenegraph/qsgbasicimagenode_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSGBASICIMAGENODE_P_H +#define QSGBASICIMAGENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGBasicImageNode : public QSGImageNode +{ +public: + QSGBasicImageNode(); + + void setTargetRect(const QRectF &rect) override; + void setInnerTargetRect(const QRectF &rect) override; + void setInnerSourceRect(const QRectF &rect) override; + void setSubSourceRect(const QRectF &rect) override; + void setTexture(QSGTexture *texture) override; + void setAntialiasing(bool antialiasing) override; + void setMirror(bool mirror) override; + void update() override; + void preprocess() override; + +protected: + virtual void updateMaterialAntialiasing() = 0; + virtual void setMaterialTexture(QSGTexture *texture) = 0; + virtual QSGTexture *materialTexture() const = 0; + virtual bool updateMaterialBlending() = 0; + virtual bool supportsWrap(const QSize &size) const = 0; + + void updateGeometry(); + + QRectF m_targetRect; + QRectF m_innerTargetRect; + QRectF m_innerSourceRect; + QRectF m_subSourceRect; + + uint m_antialiasing : 1; + uint m_mirror : 1; + uint m_dirtyGeometry : 1; + + QSGGeometry m_geometry; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/qsgbasicrectanglenode.cpp b/src/quick/scenegraph/qsgbasicrectanglenode.cpp new file mode 100644 index 0000000000..14d2dc9677 --- /dev/null +++ b/src/quick/scenegraph/qsgbasicrectanglenode.cpp @@ -0,0 +1,687 @@ +/**************************************************************************** +** +** 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 "qsgbasicrectanglenode_p.h" + +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +namespace +{ + struct Color4ub + { + unsigned char r, g, b, a; + }; + + Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; } + Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; } + + inline Color4ub colorToColor4ub(const QColor &c) + { + Color4ub color = { uchar(qRound(c.redF() * c.alphaF() * 255)), + uchar(qRound(c.greenF() * c.alphaF() * 255)), + uchar(qRound(c.blueF() * c.alphaF() * 255)), + uchar(qRound(c.alphaF() * 255)) + }; + return color; + } + + // Same layout as QSGGeometry::ColoredPoint2D, but uses Color4ub for convenience. + struct Vertex + { + float x, y; + Color4ub color; + void set(float nx, float ny, Color4ub ncolor) + { + x = nx; y = ny; color = ncolor; + } + }; + + struct SmoothVertex : public Vertex + { + float dx, dy; + void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy) + { + Vertex::set(nx, ny, ncolor); + dx = ndx; dy = ndy; + } + }; + + const QSGGeometry::AttributeSet &smoothAttributeSet() + { + static QSGGeometry::Attribute data[] = { + QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION), + QSGGeometry::Attribute::createWithSemantic(1, 4, QSGGeometry::TypeUnsignedByte, QSGGeometry::Attribute::COLOR), + QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD) + }; + static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data }; + return attrs; + } +} + +QSGBasicRectangleNode::QSGBasicRectangleNode() + : m_radius(0) + , m_pen_width(0) + , m_aligned(true) + , m_antialiasing(false) + , m_gradient_is_opaque(true) + , m_dirty_geometry(false) + , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0) +{ + setGeometry(&m_geometry); + +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("rectangle")); +#endif +} + +void QSGBasicRectangleNode::setRect(const QRectF &rect) +{ + if (rect == m_rect) + return; + m_rect = rect; + m_dirty_geometry = true; +} + +void QSGBasicRectangleNode::setColor(const QColor &color) +{ + if (color == m_color) + return; + m_color = color; + if (m_gradient_stops.isEmpty()) + m_dirty_geometry = true; +} + +void QSGBasicRectangleNode::setPenColor(const QColor &color) +{ + if (color == m_border_color) + return; + m_border_color = color; + if (m_pen_width > 0) + m_dirty_geometry = true; +} + +void QSGBasicRectangleNode::setPenWidth(qreal width) +{ + if (width == m_pen_width) + return; + m_pen_width = width; + m_dirty_geometry = true; +} + + +void QSGBasicRectangleNode::setGradientStops(const QGradientStops &stops) +{ + if (stops.constData() == m_gradient_stops.constData()) + return; + + m_gradient_stops = stops; + + m_gradient_is_opaque = true; + for (int i = 0; i < stops.size(); ++i) + m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff; + m_dirty_geometry = true; +} + +void QSGBasicRectangleNode::setRadius(qreal radius) +{ + if (radius == m_radius) + return; + m_radius = radius; + m_dirty_geometry = true; +} + +void QSGBasicRectangleNode::setAntialiasing(bool antialiasing) +{ + if (!supportsAntialiasing()) + return; + + if (antialiasing == m_antialiasing) + return; + m_antialiasing = antialiasing; + if (m_antialiasing) { + setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); + setFlag(OwnsGeometry, true); + } else { + setGeometry(&m_geometry); + setFlag(OwnsGeometry, false); + } + updateMaterialAntialiasing(); + m_dirty_geometry = true; +} + +void QSGBasicRectangleNode::setAligned(bool aligned) +{ + if (aligned == m_aligned) + return; + m_aligned = aligned; + m_dirty_geometry = true; +} + +void QSGBasicRectangleNode::update() +{ + if (m_dirty_geometry) { + updateGeometry(); + m_dirty_geometry = false; + + QSGNode::DirtyState state = QSGNode::DirtyGeometry; + updateMaterialBlending(&state); + markDirty(state); + } +} + +void QSGBasicRectangleNode::updateGeometry() +{ + float width = float(m_rect.width()); + float height = float(m_rect.height()); + float penWidth = qMin(qMin(width, height) * 0.5f, float(m_pen_width)); + + if (m_aligned) + penWidth = qRound(penWidth); + + QSGGeometry *g = geometry(); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + int vertexStride = g->sizeOfVertex(); + + union { + Vertex *vertices; + SmoothVertex *smoothVertices; + }; + + Color4ub fillColor = colorToColor4ub(m_color); + Color4ub borderColor = colorToColor4ub(m_border_color); + Color4ub transparent = { 0, 0, 0, 0 }; + const QGradientStops &stops = m_gradient_stops; + + int nextGradientStop = 0; + float gradientPos = penWidth / height; + while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) + ++nextGradientStop; + int lastGradientStop = stops.size() - 1; + float lastGradientPos = 1.0f - penWidth / height; + while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos) + --lastGradientStop; + int gradientIntersections = (lastGradientStop - nextGradientStop + 1); + + if (m_radius > 0) { + // Rounded corners. + + // Radius should never exceeds half of the width or half of the height + float radius = qMin(qMin(width, height) * 0.5f, float(m_radius)); + QRectF innerRect = m_rect; + innerRect.adjust(radius, radius, -radius, -radius); + + float innerRadius = radius - penWidth * 1.0f; + float outerRadius = radius; + float delta = qMin(width, height) * 0.5f; + + // Number of segments per corner, approximately one per 3 pixels. + int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18); + + /* + + --+--__ + --+--__--__ + | --__--__ + | seg --__--+ + --+-__ ment _+ \ + --+-__--__ - \ \ + --__--+ se \ \ + + \ g \ \ + \ \ m \ \ + -----------+--+ e \ \ <- gradient line + \ \ nt\ \ + fill +--+----+--+ + | | | | + border + inner AA outer AA (AA = antialiasing) + + */ + + int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2; + int outerVertexCount = (segments + 1) * 4; + int vertexCount = innerVertexCount; + if (m_antialiasing || penWidth) + vertexCount += innerVertexCount; + if (penWidth) + vertexCount += outerVertexCount; + if (m_antialiasing && penWidth) + vertexCount += outerVertexCount; + + int fillIndexCount = innerVertexCount; + int innerAAIndexCount = innerVertexCount * 2 + 2; + int borderIndexCount = innerVertexCount * 2 + 2; + int outerAAIndexCount = outerVertexCount * 2 + 2; + int indexCount = 0; + int fillHead = 0; + int innerAAHead = 0; + int innerAATail = 0; + int borderHead = 0; + int borderTail = 0; + int outerAAHead = 0; + int outerAATail = 0; + bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); + if (hasFill) + indexCount += fillIndexCount; + if (m_antialiasing) { + innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; + indexCount += innerAAIndexCount; + } + if (penWidth) { + borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; + indexCount += borderIndexCount; + } + if (m_antialiasing && penWidth) { + outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; + indexCount += outerAAIndexCount; + } + + g->allocate(vertexCount, indexCount); + vertices = reinterpret_cast<Vertex *>(g->vertexData()); + memset(vertices, 0, vertexCount * vertexStride); + quint16 *indices = g->indexDataAsUShort(); + quint16 index = 0; + + float py = 0; // previous inner y-coordinate. + float plx = 0; // previous inner left x-coordinate. + float prx = 0; // previous inner right x-coordinate. + + float angle = 0.5f * float(M_PI) / segments; + float cosStep = qFastCos(angle); + float sinStep = qFastSin(angle); + + for (int part = 0; part < 2; ++part) { + float c = 1 - part; + float s = part; + for (int i = 0; i <= segments; ++i) { + float y, lx, rx; + if (innerRadius > 0) { + y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate. + lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate. + rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate. + gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height; + } else { + y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate. + lx = innerRect.left() - innerRadius; // current inner left x-coordinate. + rx = innerRect.right() + innerRadius; // current inner right x-coordinate. + gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height; + } + float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate. + float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate. + float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate. + + while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { + // Insert vertices at gradient stops. + float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height; + float t = (gy - py) / (y - py); + float glx = plx * (1 - t) + t * lx; + float grx = prx * (1 - t) + t * rx; + + fillColor = colorToColor4ub(stops.at(nextGradientStop).second); + + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; + } + + if (penWidth) { + --borderHead; + indices[borderHead] = indices[borderHead + 2]; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail] = indices[borderTail - 2]; + ++borderTail; + } + + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + bool lower = stops.at(nextGradientStop).first > 0.5f; + float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); + smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy); + smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy); + if (penWidth) { + smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); + smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); + } else { + dy = lower ? delta : -delta; + smoothVertices[index++].set(grx, gy, transparent, delta, dy); + smoothVertices[index++].set(glx, gy, transparent, -delta, dy); + } + } else { + vertices[index++].set(grx, gy, fillColor); + vertices[index++].set(glx, gy, fillColor); + if (penWidth) { + vertices[index++].set(grx, gy, borderColor); + vertices[index++].set(glx, gy, borderColor); + } + } + ++nextGradientStop; + } + + if (!stops.isEmpty()) { + if (nextGradientStop == 0) { + fillColor = colorToColor4ub(stops.at(0).second); + } else if (nextGradientStop == stops.size()) { + fillColor = colorToColor4ub(stops.last().second); + } else { + const QGradientStop &prev = stops.at(nextGradientStop - 1); + const QGradientStop &next = stops.at(nextGradientStop); + float t = (gradientPos - prev.first) / (next.first - prev.first); + fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t; + } + } + + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; + } + + if (penWidth) { + indices[--borderHead] = index + 4; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail++] = index + 5; + } + + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); + smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); + smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); + + dy = part ? delta : -delta; + if (penWidth) { + smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); + smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); + smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c); + smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c); + smoothVertices[index++].set(rX, Y, transparent, delta, dy); + smoothVertices[index++].set(lX, Y, transparent, -delta, dy); + + indices[--outerAAHead] = index - 2; + indices[--outerAAHead] = index - 4; + indices[outerAATail++] = index - 3; + indices[outerAATail++] = index - 1; + } else { + smoothVertices[index++].set(rx, y, transparent, delta, dy); + smoothVertices[index++].set(lx, y, transparent, -delta, dy); + } + } else { + vertices[index++].set(rx, y, fillColor); + vertices[index++].set(lx, y, fillColor); + if (penWidth) { + vertices[index++].set(rx, y, borderColor); + vertices[index++].set(lx, y, borderColor); + vertices[index++].set(rX, Y, borderColor); + vertices[index++].set(lX, Y, borderColor); + } + } + + py = y; + plx = lx; + prx = rx; + + // Rotate + qreal tmp = c; + c = c * cosStep - s * sinStep; + s = s * cosStep + tmp * sinStep; + } + } + Q_ASSERT(index == vertexCount); + + // Close the triangle strips. + if (m_antialiasing) { + indices[--innerAAHead] = indices[innerAATail - 1]; + indices[--innerAAHead] = indices[innerAATail - 2]; + Q_ASSERT(innerAATail <= indexCount); + } + if (penWidth) { + indices[--borderHead] = indices[borderTail - 1]; + indices[--borderHead] = indices[borderTail - 2]; + Q_ASSERT(borderTail <= indexCount); + } + if (m_antialiasing && penWidth) { + indices[--outerAAHead] = indices[outerAATail - 1]; + indices[--outerAAHead] = indices[outerAATail - 2]; + Q_ASSERT(outerAATail == indexCount); + } + } else { + // Straight corners. + QRectF innerRect = m_rect; + QRectF outerRect = m_rect; + + if (penWidth) + innerRect.adjust(1.0f * penWidth, 1.0f * penWidth, -1.0f * penWidth, -1.0f * penWidth); + + float delta = qMin(width, height) * 0.5f; + int innerVertexCount = 4 + gradientIntersections * 2; + int outerVertexCount = 4; + int vertexCount = innerVertexCount; + if (m_antialiasing || penWidth) + vertexCount += innerVertexCount; + if (penWidth) + vertexCount += outerVertexCount; + if (m_antialiasing && penWidth) + vertexCount += outerVertexCount; + + int fillIndexCount = innerVertexCount; + int innerAAIndexCount = innerVertexCount * 2 + 2; + int borderIndexCount = innerVertexCount * 2 + 2; + int outerAAIndexCount = outerVertexCount * 2 + 2; + int indexCount = 0; + int fillHead = 0; + int innerAAHead = 0; + int innerAATail = 0; + int borderHead = 0; + int borderTail = 0; + int outerAAHead = 0; + int outerAATail = 0; + bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); + if (hasFill) + indexCount += fillIndexCount; + if (m_antialiasing) { + innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; + indexCount += innerAAIndexCount; + } + if (penWidth) { + borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; + indexCount += borderIndexCount; + } + if (m_antialiasing && penWidth) { + outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; + indexCount += outerAAIndexCount; + } + + g->allocate(vertexCount, indexCount); + vertices = reinterpret_cast<Vertex *>(g->vertexData()); + memset(vertices, 0, vertexCount * vertexStride); + quint16 *indices = g->indexDataAsUShort(); + quint16 index = 0; + + float lx = innerRect.left(); + float rx = innerRect.right(); + float lX = outerRect.left(); + float rX = outerRect.right(); + + for (int part = -1; part <= 1; part += 2) { + float y = (part == 1 ? innerRect.bottom() : innerRect.top()); + float Y = (part == 1 ? outerRect.bottom() : outerRect.top()); + gradientPos = (y - innerRect.top() + penWidth) / height; + + while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { + // Insert vertices at gradient stops. + float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height; + + fillColor = colorToColor4ub(stops.at(nextGradientStop).second); + + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; + } + + if (penWidth) { + --borderHead; + indices[borderHead] = indices[borderHead + 2]; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail] = indices[borderTail - 2]; + ++borderTail; + } + + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + bool lower = stops.at(nextGradientStop).first > 0.5f; + float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); + smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy); + smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy); + if (penWidth) { + smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); + smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); + } else { + smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta); + smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta); + } + } else { + vertices[index++].set(rx, gy, fillColor); + vertices[index++].set(lx, gy, fillColor); + if (penWidth) { + vertices[index++].set(rx, gy, borderColor); + vertices[index++].set(lx, gy, borderColor); + } + } + ++nextGradientStop; + } + + if (!stops.isEmpty()) { + if (nextGradientStop == 0) { + fillColor = colorToColor4ub(stops.at(0).second); + } else if (nextGradientStop == stops.size()) { + fillColor = colorToColor4ub(stops.last().second); + } else { + const QGradientStop &prev = stops.at(nextGradientStop - 1); + const QGradientStop &next = stops.at(nextGradientStop); + float t = (gradientPos - prev.first) / (next.first - prev.first); + fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t; + } + } + + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; + } + + if (penWidth) { + indices[--borderHead] = index + 4; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail++] = index + 5; + } + + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); + smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); + smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); + + if (penWidth) { + smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part); + smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part); + smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part); + smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part); + smoothVertices[index++].set(rX, Y, transparent, delta, delta * part); + smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part); + + indices[--outerAAHead] = index - 2; + indices[--outerAAHead] = index - 4; + indices[outerAATail++] = index - 3; + indices[outerAATail++] = index - 1; + } else { + smoothVertices[index++].set(rx, y, transparent, delta, delta * part); + smoothVertices[index++].set(lx, y, transparent, -delta, delta * part); + } + } else { + vertices[index++].set(rx, y, fillColor); + vertices[index++].set(lx, y, fillColor); + if (penWidth) { + vertices[index++].set(rx, y, borderColor); + vertices[index++].set(lx, y, borderColor); + vertices[index++].set(rX, Y, borderColor); + vertices[index++].set(lX, Y, borderColor); + } + } + } + Q_ASSERT(index == vertexCount); + + // Close the triangle strips. + if (m_antialiasing) { + indices[--innerAAHead] = indices[innerAATail - 1]; + indices[--innerAAHead] = indices[innerAATail - 2]; + Q_ASSERT(innerAATail <= indexCount); + } + if (penWidth) { + indices[--borderHead] = indices[borderTail - 1]; + indices[--borderHead] = indices[borderTail - 2]; + Q_ASSERT(borderTail <= indexCount); + } + if (m_antialiasing && penWidth) { + indices[--outerAAHead] = indices[outerAATail - 1]; + indices[--outerAAHead] = indices[outerAATail - 2]; + Q_ASSERT(outerAATail == indexCount); + } + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgbasicrectanglenode_p.h b/src/quick/scenegraph/qsgbasicrectanglenode_p.h new file mode 100644 index 0000000000..b1d1457590 --- /dev/null +++ b/src/quick/scenegraph/qsgbasicrectanglenode_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + + +#ifndef QSGBASICRECTANGLENODE_P_H +#define QSGBASICRECTANGLENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGBasicRectangleNode : public QSGRectangleNode +{ +public: + QSGBasicRectangleNode(); + + void setRect(const QRectF &rect) override; + void setColor(const QColor &color) override; + void setPenColor(const QColor &color) override; + void setPenWidth(qreal width) override; + void setGradientStops(const QGradientStops &stops) override; + void setRadius(qreal radius) override; + void setAntialiasing(bool antialiasing) override; + void setAligned(bool aligned) override; + void update() override; + +protected: + virtual bool supportsAntialiasing() const { return true; } + virtual void updateMaterialAntialiasing() = 0; + virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0; + + void updateGeometry(); + void updateGradientTexture(); + + QRectF m_rect; + QGradientStops m_gradient_stops; + QColor m_color; + QColor m_border_color; + qreal m_radius; + qreal m_pen_width; + + uint m_aligned : 1; + uint m_antialiasing : 1; + uint m_gradient_is_opaque : 1; + uint m_dirty_geometry : 1; + + QSGGeometry m_geometry; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp index 082a4f8c09..b856d99bc1 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp @@ -40,59 +40,11 @@ #include "qsgdefaultglyphnode_p.h" #include "qsgdefaultglyphnode_p_p.h" -#include <qopenglshaderprogram.h> -#include <private/qfont_p.h> - QT_BEGIN_NAMESPACE -QSGDefaultGlyphNode::QSGDefaultGlyphNode() - : m_style(QQuickText::Normal) - , m_material(0) - , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) -{ - m_geometry.setDrawingMode(GL_TRIANGLES); - setGeometry(&m_geometry); -} - -QSGDefaultGlyphNode::~QSGDefaultGlyphNode() -{ - delete m_material; -} - -void QSGDefaultGlyphNode::setColor(const QColor &color) -{ - m_color = color; - if (m_material != 0) { - m_material->setColor(color); - markDirty(DirtyMaterial); - } -} - -void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) -{ - if (m_material != 0) - delete m_material; - - m_position = position; - m_glyphs = glyphs; - -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("glyphs")); -#endif -} - -void QSGDefaultGlyphNode::setStyle(QQuickText::TextStyle style) -{ - if (m_style == style) - return; - m_style = style; -} - -void QSGDefaultGlyphNode::setStyleColor(const QColor &color) +void QSGDefaultGlyphNode::setMaterialColor(const QColor &color) { - if (m_styleColor == color) - return; - m_styleColor = color; + static_cast<QSGTextMaskMaterial *>(m_material)->setColor(color); } void QSGDefaultGlyphNode::update() @@ -120,11 +72,12 @@ void QSGDefaultGlyphNode::update() m_material = material; } - m_material->setColor(m_color); + QSGTextMaskMaterial *textMaskMaterial = static_cast<QSGTextMaskMaterial *>(m_material); + textMaskMaterial->setColor(m_color); QRectF boundingRect; - m_material->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(), - &boundingRect, &m_baseLine, margins); + textMaskMaterial->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(), + &boundingRect, &m_baseLine, margins); setBoundingRect(boundingRect); setMaterial(m_material); diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h index 4efeaea373..0eb7a4e4bd 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h @@ -52,39 +52,15 @@ // #include <private/qsgadaptationlayer_p.h> -#include <QtQuick/qsgnode.h> +#include <private/qsgbasicglyphnode_p.h> QT_BEGIN_NAMESPACE -class QGlyphs; -class QSGTextMaskMaterial; -class QSGDefaultGlyphNode: public QSGGlyphNode +class QSGDefaultGlyphNode : public QSGBasicGlyphNode { public: - QSGDefaultGlyphNode(); - virtual ~QSGDefaultGlyphNode(); - - virtual QPointF baseLine() const { return m_baseLine; } - virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs); - virtual void setColor(const QColor &color); - - virtual void setPreferredAntialiasingMode(AntialiasingMode) { } - virtual void setStyle(QQuickText::TextStyle); - virtual void setStyleColor(const QColor &); - - virtual void update(); - -protected: - QGlyphRun m_glyphs; - QPointF m_position; - QColor m_color; - QQuickText::TextStyle m_style; - QColor m_styleColor; - - QPointF m_baseLine; - QSGTextMaskMaterial *m_material; - - QSGGeometry m_geometry; + void setMaterialColor(const QColor &color) override; + void update() override; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultimagenode.cpp b/src/quick/scenegraph/qsgdefaultimagenode.cpp index 8e61bf9ec2..8ab5575b18 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode.cpp +++ b/src/quick/scenegraph/qsgdefaultimagenode.cpp @@ -39,38 +39,12 @@ #include "qsgdefaultimagenode_p.h" #include <private/qsgmaterialshader_p.h> - -#include <QtCore/qvarlengtharray.h> -#include <QtCore/qmath.h> -#include <QtGui/qopenglfunctions.h> - -#include <qsgtexturematerial.h> #include <private/qsgtexturematerial_p.h> -#include <qsgmaterial.h> +#include <QtGui/qopenglfunctions.h> +#include <QtCore/qmath.h> QT_BEGIN_NAMESPACE -namespace -{ - struct SmoothVertex - { - float x, y, u, v; - float dx, dy, du, dv; - }; - - const QSGGeometry::AttributeSet &smoothAttributeSet() - { - static QSGGeometry::Attribute data[] = { - QSGGeometry::Attribute::createWithSemantic(0, 2, GL_FLOAT, QSGGeometry::Attribute::POSITION), - QSGGeometry::Attribute::createWithSemantic(1, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD), - QSGGeometry::Attribute::createWithSemantic(2, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD1), - QSGGeometry::Attribute::createWithSemantic(3, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD2) - }; - static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data }; - return attrs; - } -} - class SmoothTextureMaterialShader : public QSGTextureMaterialShader { public: @@ -143,463 +117,6 @@ void SmoothTextureMaterialShader::initialize() QSGTextureMaterialShader::initialize(); } -QSGDefaultNoMaterialImageNode::QSGDefaultNoMaterialImageNode() - : m_innerSourceRect(0, 0, 1, 1) - , m_subSourceRect(0, 0, 1, 1) - , m_antialiasing(false) - , m_mirror(false) - , m_dirtyGeometry(false) - , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) -{ - setGeometry(&m_geometry); - -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("image")); -#endif -} - -void QSGDefaultNoMaterialImageNode::setTargetRect(const QRectF &rect) -{ - if (rect == m_targetRect) - return; - m_targetRect = rect; - m_dirtyGeometry = true; -} - -void QSGDefaultNoMaterialImageNode::setInnerTargetRect(const QRectF &rect) -{ - if (rect == m_innerTargetRect) - return; - m_innerTargetRect = rect; - m_dirtyGeometry = true; -} - -void QSGDefaultNoMaterialImageNode::setInnerSourceRect(const QRectF &rect) -{ - if (rect == m_innerSourceRect) - return; - m_innerSourceRect = rect; - m_dirtyGeometry = true; -} - -void QSGDefaultNoMaterialImageNode::setSubSourceRect(const QRectF &rect) -{ - if (rect == m_subSourceRect) - return; - m_subSourceRect = rect; - m_dirtyGeometry = true; -} - -void QSGDefaultNoMaterialImageNode::setTexture(QSGTexture *texture) -{ - Q_ASSERT(texture); - - setMaterialTexture(texture); - updateMaterialBlending(); - - markDirty(DirtyMaterial); - - // Because the texture can be a different part of the atlas, we need to update it... - m_dirtyGeometry = true; -} - -void QSGDefaultNoMaterialImageNode::setAntialiasing(bool antialiasing) -{ - if (antialiasing == m_antialiasing) - return; - m_antialiasing = antialiasing; - if (m_antialiasing) { - setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); - setFlag(OwnsGeometry, true); - } else { - setGeometry(&m_geometry); - setFlag(OwnsGeometry, false); - } - updateMaterialAntialiasing(); - m_dirtyGeometry = true; -} - -void QSGDefaultNoMaterialImageNode::setMirror(bool mirror) -{ - if (mirror == m_mirror) - return; - m_mirror = mirror; - m_dirtyGeometry = true; -} - - -void QSGDefaultNoMaterialImageNode::update() -{ - if (m_dirtyGeometry) - updateGeometry(); -} - -void QSGDefaultNoMaterialImageNode::preprocess() -{ - bool doDirty = false; - QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(materialTexture()); - if (t) { - doDirty = t->updateTexture(); - if (doDirty) - updateGeometry(); - } - - if (updateMaterialBlending()) - doDirty = true; - - if (doDirty) - markDirty(DirtyMaterial); -} - -inline static bool isPowerOfTwo(int x) -{ - // Assumption: x >= 1 - return x == (x & -x); -} - -namespace { - struct X { float x, tx; }; - struct Y { float y, ty; }; -} - -static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight, - quint16 bottomLeft, quint16 bottomRight) -{ - *(*indices)++ = topLeft; - *(*indices)++ = bottomLeft; - *(*indices)++ = bottomRight; - *(*indices)++ = bottomRight; - *(*indices)++ = topRight; - *(*indices)++ = topLeft; -} - -void QSGDefaultNoMaterialImageNode::updateGeometry() -{ - Q_ASSERT(!m_targetRect.isEmpty()); - const QSGTexture *t = materialTexture(); - if (!t) { - QSGGeometry *g = geometry(); - g->allocate(4); - g->setDrawingMode(GL_TRIANGLE_STRIP); - memset(g->vertexData(), 0, g->sizeOfVertex() * 4); - } else { - QRectF sourceRect = t->normalizedTextureSubRect(); - - QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(), - sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(), - m_innerSourceRect.width() * sourceRect.width(), - m_innerSourceRect.height() * sourceRect.height()); - - bool hasMargins = m_targetRect != m_innerTargetRect; - - int floorLeft = qFloor(m_subSourceRect.left()); - int ceilRight = qCeil(m_subSourceRect.right()); - int floorTop = qFloor(m_subSourceRect.top()); - int ceilBottom = qCeil(m_subSourceRect.bottom()); - int hTiles = ceilRight - floorLeft; - int vTiles = ceilBottom - floorTop; - - bool hasTiles = hTiles != 1 || vTiles != 1; - bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1); - - // An image can be rendered as a single quad if: - // - There are no margins, and either: - // - the image isn't repeated - // - the source rectangle fills the entire texture so that texture wrapping can be used, - // and NPOT is supported - if (!hasMargins && (!hasTiles || (fullTexture && supportsWrap(t->textureSize())))) { - QRectF sr; - if (!fullTexture) { - sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(), - innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(), - m_subSourceRect.width() * innerSourceRect.width(), - m_subSourceRect.height() * innerSourceRect.height()); - } else { - sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop, - m_subSourceRect.width(), m_subSourceRect.height()); - } - if (m_mirror) { - qreal oldLeft = sr.left(); - sr.setLeft(sr.right()); - sr.setRight(oldLeft); - } - - if (m_antialiasing) { - QSGGeometry *g = geometry(); - Q_ASSERT(g != &m_geometry); - g->allocate(8, 14); - g->setDrawingMode(GL_TRIANGLE_STRIP); - SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); - float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) - ? m_targetRect.width() : m_targetRect.height()) * 0.5f; - float sx = float(sr.width() / m_targetRect.width()); - float sy = float(sr.height() / m_targetRect.height()); - for (int d = -1; d <= 1; d += 2) { - for (int j = 0; j < 2; ++j) { - for (int i = 0; i < 2; ++i, ++vertices) { - vertices->x = m_targetRect.x() + i * m_targetRect.width(); - vertices->y = m_targetRect.y() + j * m_targetRect.height(); - vertices->u = sr.x() + i * sr.width(); - vertices->v = sr.y() + j * sr.height(); - vertices->dx = (i == 0 ? delta : -delta) * d; - vertices->dy = (j == 0 ? delta : -delta) * d; - vertices->du = (d < 0 ? 0 : vertices->dx * sx); - vertices->dv = (d < 0 ? 0 : vertices->dy * sy); - } - } - } - Q_ASSERT(vertices - g->vertexCount() == g->vertexData()); - static const quint16 indices[] = { - 0, 4, 1, 5, 3, 7, 2, 6, 0, 4, - 4, 6, 5, 7 - }; - Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices)); - memcpy(g->indexDataAsUShort(), indices, sizeof(indices)); - } else { - m_geometry.allocate(4); - m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); - QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); - } - } else { - int hCells = hTiles; - int vCells = vTiles; - if (m_innerTargetRect.width() == 0) - hCells = 0; - if (m_innerTargetRect.left() != m_targetRect.left()) - ++hCells; - if (m_innerTargetRect.right() != m_targetRect.right()) - ++hCells; - if (m_innerTargetRect.height() == 0) - vCells = 0; - if (m_innerTargetRect.top() != m_targetRect.top()) - ++vCells; - if (m_innerTargetRect.bottom() != m_targetRect.bottom()) - ++vCells; - QVarLengthArray<X, 32> xData(2 * hCells); - QVarLengthArray<Y, 32> yData(2 * vCells); - X *xs = xData.data(); - Y *ys = yData.data(); - - if (m_innerTargetRect.left() != m_targetRect.left()) { - xs[0].x = m_targetRect.left(); - xs[0].tx = sourceRect.left(); - xs[1].x = m_innerTargetRect.left(); - xs[1].tx = innerSourceRect.left(); - xs += 2; - } - if (m_innerTargetRect.width() != 0) { - xs[0].x = m_innerTargetRect.left(); - xs[0].tx = innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(); - ++xs; - float b = m_innerTargetRect.width() / m_subSourceRect.width(); - float a = m_innerTargetRect.x() - m_subSourceRect.x() * b; - for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { - xs[0].x = xs[1].x = a + b * i; - xs[0].tx = innerSourceRect.right(); - xs[1].tx = innerSourceRect.left(); - xs += 2; - } - xs[0].x = m_innerTargetRect.right(); - xs[0].tx = innerSourceRect.x() + (m_subSourceRect.right() - ceilRight + 1) * innerSourceRect.width(); - ++xs; - } - if (m_innerTargetRect.right() != m_targetRect.right()) { - xs[0].x = m_innerTargetRect.right(); - xs[0].tx = innerSourceRect.right(); - xs[1].x = m_targetRect.right(); - xs[1].tx = sourceRect.right(); - xs += 2; - } - Q_ASSERT(xs == xData.data() + xData.size()); - if (m_mirror) { - float leftPlusRight = m_targetRect.left() + m_targetRect.right(); - int count = xData.size(); - xs = xData.data(); - for (int i = 0; i < count >> 1; ++i) - qSwap(xs[i], xs[count - 1 - i]); - for (int i = 0; i < count; ++i) - xs[i].x = leftPlusRight - xs[i].x; - } - - if (m_innerTargetRect.top() != m_targetRect.top()) { - ys[0].y = m_targetRect.top(); - ys[0].ty = sourceRect.top(); - ys[1].y = m_innerTargetRect.top(); - ys[1].ty = innerSourceRect.top(); - ys += 2; - } - if (m_innerTargetRect.height() != 0) { - ys[0].y = m_innerTargetRect.top(); - ys[0].ty = innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(); - ++ys; - float b = m_innerTargetRect.height() / m_subSourceRect.height(); - float a = m_innerTargetRect.y() - m_subSourceRect.y() * b; - for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { - ys[0].y = ys[1].y = a + b * i; - ys[0].ty = innerSourceRect.bottom(); - ys[1].ty = innerSourceRect.top(); - ys += 2; - } - ys[0].y = m_innerTargetRect.bottom(); - ys[0].ty = innerSourceRect.y() + (m_subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height(); - ++ys; - } - if (m_innerTargetRect.bottom() != m_targetRect.bottom()) { - ys[0].y = m_innerTargetRect.bottom(); - ys[0].ty = innerSourceRect.bottom(); - ys[1].y = m_targetRect.bottom(); - ys[1].ty = sourceRect.bottom(); - ys += 2; - } - Q_ASSERT(ys == yData.data() + yData.size()); - - if (m_antialiasing) { - QSGGeometry *g = geometry(); - Q_ASSERT(g != &m_geometry); - - g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, - hCells * vCells * 6 + (hCells + vCells) * 12); - g->setDrawingMode(GL_TRIANGLES); - SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); - memset(vertices, 0, g->vertexCount() * g->sizeOfVertex()); - quint16 *indices = g->indexDataAsUShort(); - - // The deltas are how much the fuzziness can reach into the image. - // Only the border vertices are moved by the vertex shader, so the fuzziness - // can't reach further into the image than the closest interior vertices. - float leftDx = xData.at(1).x - xData.at(0).x; - float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x; - float topDy = yData.at(1).y - yData.at(0).y; - float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y; - - float leftDu = xData.at(1).tx - xData.at(0).tx; - float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx; - float topDv = yData.at(1).ty - yData.at(0).ty; - float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty; - - if (hCells == 1) { - leftDx = rightDx *= 0.5f; - leftDu = rightDu *= 0.5f; - } - if (vCells == 1) { - topDy = bottomDy *= 0.5f; - topDv = bottomDv *= 0.5f; - } - - // This delta is how much the fuzziness can reach out from the image. - float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) - ? m_targetRect.width() : m_targetRect.height()) * 0.5f; - - quint16 index = 0; - ys = yData.data(); - for (int j = 0; j < vCells; ++j, ys += 2) { - xs = xData.data(); - bool isTop = j == 0; - bool isBottom = j == vCells - 1; - for (int i = 0; i < hCells; ++i, xs += 2) { - bool isLeft = i == 0; - bool isRight = i == hCells - 1; - - SmoothVertex *v = vertices + index; - - quint16 topLeft = index; - for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) { - v->x = xs[0].x; - v->u = xs[0].tx; - v->y = ys[0].y; - v->v = ys[0].ty; - } - - quint16 topRight = index; - for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) { - v->x = xs[1].x; - v->u = xs[1].tx; - v->y = ys[0].y; - v->v = ys[0].ty; - } - - quint16 bottomLeft = index; - for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) { - v->x = xs[0].x; - v->u = xs[0].tx; - v->y = ys[1].y; - v->v = ys[1].ty; - } - - quint16 bottomRight = index; - for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) { - v->x = xs[1].x; - v->u = xs[1].tx; - v->y = ys[1].y; - v->v = ys[1].ty; - } - - appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight); - - if (isTop) { - vertices[topLeft].dy = vertices[topRight].dy = topDy; - vertices[topLeft].dv = vertices[topRight].dv = topDv; - vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta; - appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight); - } - - if (isBottom) { - vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy; - vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv; - vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta; - appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1); - } - - if (isLeft) { - vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx; - vertices[topLeft].du = vertices[bottomLeft].du = leftDu; - vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta; - appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft); - } - - if (isRight) { - vertices[topRight].dx = vertices[bottomRight].dx = -rightDx; - vertices[topRight].du = vertices[bottomRight].du = -rightDu; - vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta; - appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1); - } - } - } - - Q_ASSERT(index == g->vertexCount()); - Q_ASSERT(indices - g->indexCount() == g->indexData()); - } else { - m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6); - m_geometry.setDrawingMode(GL_TRIANGLES); - QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D(); - ys = yData.data(); - for (int j = 0; j < vCells; ++j, ys += 2) { - xs = xData.data(); - for (int i = 0; i < hCells; ++i, xs += 2) { - vertices[0].x = vertices[2].x = xs[0].x; - vertices[0].tx = vertices[2].tx = xs[0].tx; - vertices[1].x = vertices[3].x = xs[1].x; - vertices[1].tx = vertices[3].tx = xs[1].tx; - - vertices[0].y = vertices[1].y = ys[0].y; - vertices[0].ty = vertices[1].ty = ys[0].ty; - vertices[2].y = vertices[3].y = ys[1].y; - vertices[2].ty = vertices[3].ty = ys[1].ty; - - vertices += 4; - } - } - - quint16 *indices = m_geometry.indexDataAsUShort(); - for (int i = 0; i < 4 * vCells * hCells; i += 4) - appendQuad(&indices, i, i + 1, i + 2, i + 3); - } - } - } - markDirty(DirtyGeometry); - m_dirtyGeometry = false; -} - QSGDefaultImageNode::QSGDefaultImageNode() { setMaterial(&m_materialO); @@ -683,6 +200,12 @@ bool QSGDefaultImageNode::updateMaterialBlending() return false; } +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + bool QSGDefaultImageNode::supportsWrap(const QSize &size) const { bool wrapSupported = true; diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h index 85de09b6eb..688c5a5039 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode_p.h +++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h @@ -53,6 +53,7 @@ // #include <private/qsgadaptationlayer_p.h> +#include <private/qsgbasicimagenode_p.h> #include <QtQuick/qsgtexturematerial.h> QT_BEGIN_NAMESPACE @@ -69,42 +70,7 @@ protected: QSGMaterialShader *createShader() const override; }; -class Q_QUICK_PRIVATE_EXPORT QSGDefaultNoMaterialImageNode : public QSGImageNode -{ -public: - QSGDefaultNoMaterialImageNode(); - void setTargetRect(const QRectF &rect) override; - void setInnerTargetRect(const QRectF &rect) override; - void setInnerSourceRect(const QRectF &rect) override; - void setSubSourceRect(const QRectF &rect) override; - void setTexture(QSGTexture *texture) override; - void setAntialiasing(bool antialiasing) override; - void setMirror(bool mirror) override; - void update() override; - void preprocess() override; - -protected: - virtual void updateMaterialAntialiasing() = 0; - virtual void setMaterialTexture(QSGTexture *texture) = 0; - virtual QSGTexture *materialTexture() const = 0; - virtual bool updateMaterialBlending() = 0; - virtual bool supportsWrap(const QSize &size) const = 0; - - void updateGeometry(); - - QRectF m_targetRect; - QRectF m_innerTargetRect; - QRectF m_innerSourceRect; - QRectF m_subSourceRect; - - uint m_antialiasing : 1; - uint m_mirror : 1; - uint m_dirtyGeometry : 1; - - QSGGeometry m_geometry; -}; - -class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGDefaultNoMaterialImageNode +class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGBasicImageNode { public: QSGDefaultImageNode(); diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp index e4a6648bbd..da1623e197 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp +++ b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp @@ -38,8 +38,6 @@ ** ****************************************************************************/ - - #include "qsgdefaultrectanglenode_p.h" #include <QtQuick/qsgvertexcolormaterial.h> @@ -52,59 +50,6 @@ QT_BEGIN_NAMESPACE -namespace -{ - struct Color4ub - { - unsigned char r, g, b, a; - }; - - Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; } - Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; } - - inline Color4ub colorToColor4ub(const QColor &c) - { - Color4ub color = { uchar(qRound(c.redF() * c.alphaF() * 255)), - uchar(qRound(c.greenF() * c.alphaF() * 255)), - uchar(qRound(c.blueF() * c.alphaF() * 255)), - uchar(qRound(c.alphaF() * 255)) - }; - return color; - } - - // Same layout as QSGGeometry::ColoredPoint2D, but uses Color4ub for convenience. - struct Vertex - { - float x, y; - Color4ub color; - void set(float nx, float ny, Color4ub ncolor) - { - x = nx; y = ny; color = ncolor; - } - }; - - struct SmoothVertex : public Vertex - { - float dx, dy; - void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy) - { - Vertex::set(nx, ny, ncolor); - dx = ndx; dy = ndy; - } - }; - - const QSGGeometry::AttributeSet &smoothAttributeSet() - { - static QSGGeometry::Attribute data[] = { - QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION), - QSGGeometry::Attribute::createWithSemantic(1, 4, QSGGeometry::TypeUnsignedByte, QSGGeometry::Attribute::COLOR), - QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD) - }; - static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data }; - return attrs; - } -} - class SmoothColorMaterialShader : public QSGMaterialShader { public: @@ -183,595 +128,6 @@ QSGMaterialShader *QSGSmoothColorMaterial::createShader() const return new SmoothColorMaterialShader; } - -QSGDefaultNoMaterialRectangleNode::QSGDefaultNoMaterialRectangleNode() - : m_radius(0) - , m_pen_width(0) - , m_aligned(true) - , m_antialiasing(false) - , m_gradient_is_opaque(true) - , m_dirty_geometry(false) - , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0) -{ - setGeometry(&m_geometry); - -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("rectangle")); -#endif -} - -void QSGDefaultNoMaterialRectangleNode::setRect(const QRectF &rect) -{ - if (rect == m_rect) - return; - m_rect = rect; - m_dirty_geometry = true; -} - -void QSGDefaultNoMaterialRectangleNode::setColor(const QColor &color) -{ - if (color == m_color) - return; - m_color = color; - if (m_gradient_stops.isEmpty()) - m_dirty_geometry = true; -} - -void QSGDefaultNoMaterialRectangleNode::setPenColor(const QColor &color) -{ - if (color == m_border_color) - return; - m_border_color = color; - if (m_pen_width > 0) - m_dirty_geometry = true; -} - -void QSGDefaultNoMaterialRectangleNode::setPenWidth(qreal width) -{ - if (width == m_pen_width) - return; - m_pen_width = width; - m_dirty_geometry = true; -} - - -void QSGDefaultNoMaterialRectangleNode::setGradientStops(const QGradientStops &stops) -{ - if (stops.constData() == m_gradient_stops.constData()) - return; - - m_gradient_stops = stops; - - m_gradient_is_opaque = true; - for (int i = 0; i < stops.size(); ++i) - m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff; - m_dirty_geometry = true; -} - -void QSGDefaultNoMaterialRectangleNode::setRadius(qreal radius) -{ - if (radius == m_radius) - return; - m_radius = radius; - m_dirty_geometry = true; -} - -void QSGDefaultNoMaterialRectangleNode::setAntialiasing(bool antialiasing) -{ - if (!supportsAntialiasing()) - return; - - if (antialiasing == m_antialiasing) - return; - m_antialiasing = antialiasing; - if (m_antialiasing) { - setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); - setFlag(OwnsGeometry, true); - } else { - setGeometry(&m_geometry); - setFlag(OwnsGeometry, false); - } - updateMaterialAntialiasing(); - m_dirty_geometry = true; -} - -void QSGDefaultNoMaterialRectangleNode::setAligned(bool aligned) -{ - if (aligned == m_aligned) - return; - m_aligned = aligned; - m_dirty_geometry = true; -} - -void QSGDefaultNoMaterialRectangleNode::update() -{ - if (m_dirty_geometry) { - updateGeometry(); - m_dirty_geometry = false; - - QSGNode::DirtyState state = QSGNode::DirtyGeometry; - updateMaterialBlending(&state); - markDirty(state); - } -} - -void QSGDefaultNoMaterialRectangleNode::updateGeometry() -{ - float width = float(m_rect.width()); - float height = float(m_rect.height()); - float penWidth = qMin(qMin(width, height) * 0.5f, float(m_pen_width)); - - if (m_aligned) - penWidth = qRound(penWidth); - - QSGGeometry *g = geometry(); - g->setDrawingMode(QSGGeometry::DrawTriangleStrip); - int vertexStride = g->sizeOfVertex(); - - union { - Vertex *vertices; - SmoothVertex *smoothVertices; - }; - - Color4ub fillColor = colorToColor4ub(m_color); - Color4ub borderColor = colorToColor4ub(m_border_color); - Color4ub transparent = { 0, 0, 0, 0 }; - const QGradientStops &stops = m_gradient_stops; - - int nextGradientStop = 0; - float gradientPos = penWidth / height; - while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) - ++nextGradientStop; - int lastGradientStop = stops.size() - 1; - float lastGradientPos = 1.0f - penWidth / height; - while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos) - --lastGradientStop; - int gradientIntersections = (lastGradientStop - nextGradientStop + 1); - - if (m_radius > 0) { - // Rounded corners. - - // Radius should never exceeds half of the width or half of the height - float radius = qMin(qMin(width, height) * 0.5f, float(m_radius)); - QRectF innerRect = m_rect; - innerRect.adjust(radius, radius, -radius, -radius); - - float innerRadius = radius - penWidth * 1.0f; - float outerRadius = radius; - float delta = qMin(width, height) * 0.5f; - - // Number of segments per corner, approximately one per 3 pixels. - int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18); - - /* - - --+--__ - --+--__--__ - | --__--__ - | seg --__--+ - --+-__ ment _+ \ - --+-__--__ - \ \ - --__--+ se \ \ - + \ g \ \ - \ \ m \ \ - -----------+--+ e \ \ <- gradient line - \ \ nt\ \ - fill +--+----+--+ - | | | | - border - inner AA outer AA (AA = antialiasing) - - */ - - int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2; - int outerVertexCount = (segments + 1) * 4; - int vertexCount = innerVertexCount; - if (m_antialiasing || penWidth) - vertexCount += innerVertexCount; - if (penWidth) - vertexCount += outerVertexCount; - if (m_antialiasing && penWidth) - vertexCount += outerVertexCount; - - int fillIndexCount = innerVertexCount; - int innerAAIndexCount = innerVertexCount * 2 + 2; - int borderIndexCount = innerVertexCount * 2 + 2; - int outerAAIndexCount = outerVertexCount * 2 + 2; - int indexCount = 0; - int fillHead = 0; - int innerAAHead = 0; - int innerAATail = 0; - int borderHead = 0; - int borderTail = 0; - int outerAAHead = 0; - int outerAATail = 0; - bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); - if (hasFill) - indexCount += fillIndexCount; - if (m_antialiasing) { - innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; - indexCount += innerAAIndexCount; - } - if (penWidth) { - borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; - indexCount += borderIndexCount; - } - if (m_antialiasing && penWidth) { - outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; - indexCount += outerAAIndexCount; - } - - g->allocate(vertexCount, indexCount); - vertices = reinterpret_cast<Vertex *>(g->vertexData()); - memset(vertices, 0, vertexCount * vertexStride); - quint16 *indices = g->indexDataAsUShort(); - quint16 index = 0; - - float py = 0; // previous inner y-coordinate. - float plx = 0; // previous inner left x-coordinate. - float prx = 0; // previous inner right x-coordinate. - - float angle = 0.5f * float(M_PI) / segments; - float cosStep = qFastCos(angle); - float sinStep = qFastSin(angle); - - for (int part = 0; part < 2; ++part) { - float c = 1 - part; - float s = part; - for (int i = 0; i <= segments; ++i) { - float y, lx, rx; - if (innerRadius > 0) { - y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate. - lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate. - rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate. - gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height; - } else { - y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate. - lx = innerRect.left() - innerRadius; // current inner left x-coordinate. - rx = innerRect.right() + innerRadius; // current inner right x-coordinate. - gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height; - } - float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate. - float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate. - float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate. - - while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { - // Insert vertices at gradient stops. - float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height; - float t = (gy - py) / (y - py); - float glx = plx * (1 - t) + t * lx; - float grx = prx * (1 - t) + t * rx; - - fillColor = colorToColor4ub(stops.at(nextGradientStop).second); - - if (hasFill) { - indices[fillHead++] = index; - indices[fillHead++] = index + 1; - } - - if (penWidth) { - --borderHead; - indices[borderHead] = indices[borderHead + 2]; - indices[--borderHead] = index + 2; - indices[borderTail++] = index + 3; - indices[borderTail] = indices[borderTail - 2]; - ++borderTail; - } - - if (m_antialiasing) { - indices[--innerAAHead] = index + 2; - indices[--innerAAHead] = index; - indices[innerAATail++] = index + 1; - indices[innerAATail++] = index + 3; - - bool lower = stops.at(nextGradientStop).first > 0.5f; - float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); - smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy); - smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy); - if (penWidth) { - smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); - smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); - } else { - dy = lower ? delta : -delta; - smoothVertices[index++].set(grx, gy, transparent, delta, dy); - smoothVertices[index++].set(glx, gy, transparent, -delta, dy); - } - } else { - vertices[index++].set(grx, gy, fillColor); - vertices[index++].set(glx, gy, fillColor); - if (penWidth) { - vertices[index++].set(grx, gy, borderColor); - vertices[index++].set(glx, gy, borderColor); - } - } - ++nextGradientStop; - } - - if (!stops.isEmpty()) { - if (nextGradientStop == 0) { - fillColor = colorToColor4ub(stops.at(0).second); - } else if (nextGradientStop == stops.size()) { - fillColor = colorToColor4ub(stops.last().second); - } else { - const QGradientStop &prev = stops.at(nextGradientStop - 1); - const QGradientStop &next = stops.at(nextGradientStop); - float t = (gradientPos - prev.first) / (next.first - prev.first); - fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t; - } - } - - if (hasFill) { - indices[fillHead++] = index; - indices[fillHead++] = index + 1; - } - - if (penWidth) { - indices[--borderHead] = index + 4; - indices[--borderHead] = index + 2; - indices[borderTail++] = index + 3; - indices[borderTail++] = index + 5; - } - - if (m_antialiasing) { - indices[--innerAAHead] = index + 2; - indices[--innerAAHead] = index; - indices[innerAATail++] = index + 1; - indices[innerAATail++] = index + 3; - - float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); - smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); - smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); - - dy = part ? delta : -delta; - if (penWidth) { - smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); - smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); - smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c); - smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c); - smoothVertices[index++].set(rX, Y, transparent, delta, dy); - smoothVertices[index++].set(lX, Y, transparent, -delta, dy); - - indices[--outerAAHead] = index - 2; - indices[--outerAAHead] = index - 4; - indices[outerAATail++] = index - 3; - indices[outerAATail++] = index - 1; - } else { - smoothVertices[index++].set(rx, y, transparent, delta, dy); - smoothVertices[index++].set(lx, y, transparent, -delta, dy); - } - } else { - vertices[index++].set(rx, y, fillColor); - vertices[index++].set(lx, y, fillColor); - if (penWidth) { - vertices[index++].set(rx, y, borderColor); - vertices[index++].set(lx, y, borderColor); - vertices[index++].set(rX, Y, borderColor); - vertices[index++].set(lX, Y, borderColor); - } - } - - py = y; - plx = lx; - prx = rx; - - // Rotate - qreal tmp = c; - c = c * cosStep - s * sinStep; - s = s * cosStep + tmp * sinStep; - } - } - Q_ASSERT(index == vertexCount); - - // Close the triangle strips. - if (m_antialiasing) { - indices[--innerAAHead] = indices[innerAATail - 1]; - indices[--innerAAHead] = indices[innerAATail - 2]; - Q_ASSERT(innerAATail <= indexCount); - } - if (penWidth) { - indices[--borderHead] = indices[borderTail - 1]; - indices[--borderHead] = indices[borderTail - 2]; - Q_ASSERT(borderTail <= indexCount); - } - if (m_antialiasing && penWidth) { - indices[--outerAAHead] = indices[outerAATail - 1]; - indices[--outerAAHead] = indices[outerAATail - 2]; - Q_ASSERT(outerAATail == indexCount); - } - } else { - // Straight corners. - QRectF innerRect = m_rect; - QRectF outerRect = m_rect; - - if (penWidth) - innerRect.adjust(1.0f * penWidth, 1.0f * penWidth, -1.0f * penWidth, -1.0f * penWidth); - - float delta = qMin(width, height) * 0.5f; - int innerVertexCount = 4 + gradientIntersections * 2; - int outerVertexCount = 4; - int vertexCount = innerVertexCount; - if (m_antialiasing || penWidth) - vertexCount += innerVertexCount; - if (penWidth) - vertexCount += outerVertexCount; - if (m_antialiasing && penWidth) - vertexCount += outerVertexCount; - - int fillIndexCount = innerVertexCount; - int innerAAIndexCount = innerVertexCount * 2 + 2; - int borderIndexCount = innerVertexCount * 2 + 2; - int outerAAIndexCount = outerVertexCount * 2 + 2; - int indexCount = 0; - int fillHead = 0; - int innerAAHead = 0; - int innerAATail = 0; - int borderHead = 0; - int borderTail = 0; - int outerAAHead = 0; - int outerAATail = 0; - bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); - if (hasFill) - indexCount += fillIndexCount; - if (m_antialiasing) { - innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; - indexCount += innerAAIndexCount; - } - if (penWidth) { - borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; - indexCount += borderIndexCount; - } - if (m_antialiasing && penWidth) { - outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; - indexCount += outerAAIndexCount; - } - - g->allocate(vertexCount, indexCount); - vertices = reinterpret_cast<Vertex *>(g->vertexData()); - memset(vertices, 0, vertexCount * vertexStride); - quint16 *indices = g->indexDataAsUShort(); - quint16 index = 0; - - float lx = innerRect.left(); - float rx = innerRect.right(); - float lX = outerRect.left(); - float rX = outerRect.right(); - - for (int part = -1; part <= 1; part += 2) { - float y = (part == 1 ? innerRect.bottom() : innerRect.top()); - float Y = (part == 1 ? outerRect.bottom() : outerRect.top()); - gradientPos = (y - innerRect.top() + penWidth) / height; - - while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { - // Insert vertices at gradient stops. - float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height; - - fillColor = colorToColor4ub(stops.at(nextGradientStop).second); - - if (hasFill) { - indices[fillHead++] = index; - indices[fillHead++] = index + 1; - } - - if (penWidth) { - --borderHead; - indices[borderHead] = indices[borderHead + 2]; - indices[--borderHead] = index + 2; - indices[borderTail++] = index + 3; - indices[borderTail] = indices[borderTail - 2]; - ++borderTail; - } - - if (m_antialiasing) { - indices[--innerAAHead] = index + 2; - indices[--innerAAHead] = index; - indices[innerAATail++] = index + 1; - indices[innerAATail++] = index + 3; - - bool lower = stops.at(nextGradientStop).first > 0.5f; - float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); - smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy); - smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy); - if (penWidth) { - smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); - smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); - } else { - smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta); - smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta); - } - } else { - vertices[index++].set(rx, gy, fillColor); - vertices[index++].set(lx, gy, fillColor); - if (penWidth) { - vertices[index++].set(rx, gy, borderColor); - vertices[index++].set(lx, gy, borderColor); - } - } - ++nextGradientStop; - } - - if (!stops.isEmpty()) { - if (nextGradientStop == 0) { - fillColor = colorToColor4ub(stops.at(0).second); - } else if (nextGradientStop == stops.size()) { - fillColor = colorToColor4ub(stops.last().second); - } else { - const QGradientStop &prev = stops.at(nextGradientStop - 1); - const QGradientStop &next = stops.at(nextGradientStop); - float t = (gradientPos - prev.first) / (next.first - prev.first); - fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t; - } - } - - if (hasFill) { - indices[fillHead++] = index; - indices[fillHead++] = index + 1; - } - - if (penWidth) { - indices[--borderHead] = index + 4; - indices[--borderHead] = index + 2; - indices[borderTail++] = index + 3; - indices[borderTail++] = index + 5; - } - - if (m_antialiasing) { - indices[--innerAAHead] = index + 2; - indices[--innerAAHead] = index; - indices[innerAATail++] = index + 1; - indices[innerAATail++] = index + 3; - - float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); - smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); - smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); - - if (penWidth) { - smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part); - smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part); - smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part); - smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part); - smoothVertices[index++].set(rX, Y, transparent, delta, delta * part); - smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part); - - indices[--outerAAHead] = index - 2; - indices[--outerAAHead] = index - 4; - indices[outerAATail++] = index - 3; - indices[outerAATail++] = index - 1; - } else { - smoothVertices[index++].set(rx, y, transparent, delta, delta * part); - smoothVertices[index++].set(lx, y, transparent, -delta, delta * part); - } - } else { - vertices[index++].set(rx, y, fillColor); - vertices[index++].set(lx, y, fillColor); - if (penWidth) { - vertices[index++].set(rx, y, borderColor); - vertices[index++].set(lx, y, borderColor); - vertices[index++].set(rX, Y, borderColor); - vertices[index++].set(lX, Y, borderColor); - } - } - } - Q_ASSERT(index == vertexCount); - - // Close the triangle strips. - if (m_antialiasing) { - indices[--innerAAHead] = indices[innerAATail - 1]; - indices[--innerAAHead] = indices[innerAATail - 2]; - Q_ASSERT(innerAATail <= indexCount); - } - if (penWidth) { - indices[--borderHead] = indices[borderTail - 1]; - indices[--borderHead] = indices[borderTail - 2]; - Q_ASSERT(borderTail <= indexCount); - } - if (m_antialiasing && penWidth) { - indices[--outerAAHead] = indices[outerAATail - 1]; - indices[--outerAAHead] = indices[outerAATail - 2]; - Q_ASSERT(outerAATail == indexCount); - } - } -} - QSGDefaultRectangleNode::QSGDefaultRectangleNode() { setMaterial(&m_material); diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h index f5f3c465b7..f30a3beed7 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h +++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h @@ -53,7 +53,7 @@ // #include <private/qsgadaptationlayer_p.h> - +#include <private/qsgbasicrectanglenode_p.h> #include <QtQuick/qsgvertexcolormaterial.h> QT_BEGIN_NAMESPACE @@ -72,45 +72,7 @@ protected: QSGMaterialShader *createShader() const override; }; -class Q_QUICK_PRIVATE_EXPORT QSGDefaultNoMaterialRectangleNode : public QSGRectangleNode -{ -public: - QSGDefaultNoMaterialRectangleNode(); - - void setRect(const QRectF &rect) override; - void setColor(const QColor &color) override; - void setPenColor(const QColor &color) override; - void setPenWidth(qreal width) override; - void setGradientStops(const QGradientStops &stops) override; - void setRadius(qreal radius) override; - void setAntialiasing(bool antialiasing) override; - void setAligned(bool aligned) override; - void update() override; - -protected: - virtual bool supportsAntialiasing() const { return true; } - virtual void updateMaterialAntialiasing() = 0; - virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0; - - void updateGeometry(); - void updateGradientTexture(); - - QRectF m_rect; - QGradientStops m_gradient_stops; - QColor m_color; - QColor m_border_color; - qreal m_radius; - qreal m_pen_width; - - uint m_aligned : 1; - uint m_antialiasing : 1; - uint m_gradient_is_opaque : 1; - uint m_dirty_geometry : 1; - - QSGGeometry m_geometry; -}; - -class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGDefaultNoMaterialRectangleNode +class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGBasicRectangleNode { public: QSGDefaultRectangleNode(); diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 33f6dff8cf..b4d8b5d231 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -79,6 +79,9 @@ HEADERS += \ $$PWD/qsgdefaultglyphnode_p_p.h \ $$PWD/qsgdefaultimagenode_p.h \ $$PWD/qsgdefaultrectanglenode_p.h \ + $$PWD/qsgbasicrectanglenode_p.h \ + $$PWD/qsgbasicimagenode_p.h \ + $$PWD/qsgbasicglyphnode_p.h \ $$PWD/qsgrenderloop_p.h \ $$PWD/qsgthreadedrenderloop_p.h \ $$PWD/qsgwindowsrenderloop_p.h \ @@ -95,6 +98,9 @@ SOURCES += \ $$PWD/qsgdistancefieldglyphnode_p.cpp \ $$PWD/qsgdefaultimagenode.cpp \ $$PWD/qsgdefaultrectanglenode.cpp \ + $$PWD/qsgbasicrectanglenode.cpp \ + $$PWD/qsgbasicimagenode.cpp \ + $$PWD/qsgbasicglyphnode.cpp \ $$PWD/qsgrenderloop.cpp \ $$PWD/qsgthreadedrenderloop.cpp \ $$PWD/qsgwindowsrenderloop.cpp \ |