diff options
32 files changed, 319 insertions, 108 deletions
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp index f475ec838b..d35f82a76a 100644 --- a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp +++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp @@ -270,4 +270,14 @@ void D3D12RenderNode::render(const RenderState *state) // No need to reimplement changedStates() because no relevant commands are // added to the command list in render(). +QSGRenderNode::RenderingFlags D3D12RenderNode::flags() const +{ + return BoundedRectRendering | DepthAwareRendering; +} + +QRectF D3D12RenderNode::rect() const +{ + return QRect(0, 0, m_item->width(), m_item->height()); +} + #endif // HAS_D3D12 diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.h b/examples/quick/scenegraph/rendernode/d3d12renderer.h index a81db0f398..f13a1d451c 100644 --- a/examples/quick/scenegraph/rendernode/d3d12renderer.h +++ b/examples/quick/scenegraph/rendernode/d3d12renderer.h @@ -60,6 +60,8 @@ public: void render(const RenderState *state) override; void releaseResources() override; + RenderingFlags flags() const override; + QRectF rect() const override; private: void init(); diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp index 33c59198b8..3de864b7b9 100644 --- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp +++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp @@ -151,4 +151,14 @@ QSGRenderNode::StateFlags OpenGLRenderNode::changedStates() const return BlendState; } +QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const +{ + return BoundedRectRendering | DepthAwareRendering; +} + +QRectF OpenGLRenderNode::rect() const +{ + return QRect(0, 0, m_item->width(), m_item->height()); +} + #endif // QT_NO_OPENGL diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.h b/examples/quick/scenegraph/rendernode/openglrenderer.h index 28d528e617..1ee6350218 100644 --- a/examples/quick/scenegraph/rendernode/openglrenderer.h +++ b/examples/quick/scenegraph/rendernode/openglrenderer.h @@ -58,6 +58,8 @@ public: void render(const RenderState *state) override; void releaseResources() override; StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; private: void init(); diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp index 2f11c56f29..06e406874a 100644 --- a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp +++ b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp @@ -68,7 +68,7 @@ void SoftwareRenderNode::render(const RenderState *renderState) p->setOpacity(inheritedOpacity()); const QRegion *clipRegion = renderState->clipRegion(); if (clipRegion && !clipRegion->isEmpty()) - p->setClipRegion(*clipRegion); + p->setClipRegion(*clipRegion, Qt::IntersectClip); const QPointF p0(m_item->width() - 1, m_item->height() - 1); const QPointF p1(0, 0); @@ -89,3 +89,13 @@ QSGRenderNode::StateFlags SoftwareRenderNode::changedStates() const { return 0; } + +QSGRenderNode::RenderingFlags SoftwareRenderNode::flags() const +{ + return BoundedRectRendering; +} + +QRectF SoftwareRenderNode::rect() const +{ + return QRect(0, 0, m_item->width(), m_item->height()); +} diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.h b/examples/quick/scenegraph/rendernode/softwarerenderer.h index 60036f96a1..5b2a475ed8 100644 --- a/examples/quick/scenegraph/rendernode/softwarerenderer.h +++ b/examples/quick/scenegraph/rendernode/softwarerenderer.h @@ -54,6 +54,8 @@ public: void render(const RenderState *state) override; void releaseResources() override; StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; private: QQuickItem *m_item; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 34ab4d11b0..e32ecdc138 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -682,9 +682,11 @@ void QSGD3D12EnginePrivate::releaseResources() commandQueue = nullptr; copyCommandQueue = nullptr; +#ifndef Q_OS_WINRT dcompTarget = nullptr; dcompVisual = nullptr; dcompDevice = nullptr; +#endif swapChain = nullptr; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h index 2b1e3dc7b7..b3b244cd86 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h @@ -435,9 +435,11 @@ private: DeviceLossTester devLossTest; +#ifndef Q_OS_WINRT ComPtr<IDCompositionDevice> dcompDevice; ComPtr<IDCompositionTarget> dcompTarget; ComPtr<IDCompositionVisual> dcompVisual; +#endif }; inline uint qHash(const QSGD3D12EnginePrivate::PersistentFrameData::PendingRelease &pr, uint seed = 0) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index f736e04b88..31250d0d57 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1359,7 +1359,7 @@ bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *prope return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr); } -QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) +QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes) { QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit; QV4::CompiledData::Unit *jsUnit = compilationUnit->createUnitData(&output); @@ -1402,6 +1402,20 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) qmlUnit->offsetToStringTable = totalSize - output.jsGenerator.stringTable.sizeOfTableAndData(); qmlUnit->stringTableSize = output.jsGenerator.stringTable.stringCount(); +#ifndef V4_BOOTSTRAP + if (!dependentTypes.isEmpty()) { + QCryptographicHash hash(QCryptographicHash::Md5); + if (dependentTypes.addToHash(&hash, engine)) { + QByteArray checksum = hash.result(); + Q_ASSERT(checksum.size() == sizeof(qmlUnit->dependencyMD5Checksum)); + memcpy(qmlUnit->dependencyMD5Checksum, checksum.constData(), sizeof(qmlUnit->dependencyMD5Checksum)); + } + } +#else + Q_UNUSED(dependentTypes); + Q_UNUSED(engine); +#endif + // write imports char *importPtr = data + qmlUnit->offsetToImports; foreach (const QV4::CompiledData::Import *imp, output.imports) { diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 821a6bba6d..eedc262e7a 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -549,7 +549,7 @@ public: struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator { - QV4::CompiledData::Unit *generate(Document &output); + QV4::CompiledData::Unit *generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes); private: typedef bool (Binding::*BindingFilter)() const; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index a0b219e28e..68de9aee44 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -156,7 +156,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() // Generate QML compiled type data structures QmlIR::QmlUnitGenerator qmlGenerator; - QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document); + QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document, QQmlEnginePrivate::get(engine), resolvedTypes); Q_ASSERT(document->javaScriptCompilationUnit); // The js unit owns the data and will free the qml unit. diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 2aab1743cc..121b5907db 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -291,6 +291,11 @@ bool CompilationUnit::saveToDisk(QString *errorString) { errorString->clear(); + if (data->sourceTimeStamp == 0) { + *errorString = QStringLiteral("Missing time stamp for source file"); + return false; + } + const QUrl unitUrl = url(); if (!unitUrl.isLocalFile()) { *errorString = QStringLiteral("File has to be a local file."); @@ -579,6 +584,19 @@ void ResolvedTypeReference::doDynamicTypeCheck() mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); } + +bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const +{ + for (auto it = constBegin(), end = constEnd(); it != end; ++it) { + QQmlPropertyCache *pc = it.value()->createPropertyCache(engine); + bool ok = false; + hash->addData(pc->checksum(&ok)); + if (!ok) + return false; + } + return true; +} + #endif } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 82e303f27d..519708f089 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -611,6 +611,7 @@ struct Unit LEUInt32 architectureIndex; // string index to QSysInfo::buildAbi() LEUInt32 codeGeneratorIndex; + char dependencyMD5Checksum[16]; enum : unsigned int { IsJavascript = 0x1, @@ -788,7 +789,14 @@ struct ResolvedTypeReference void doDynamicTypeCheck(); }; // map from name index -typedef QHash<int, ResolvedTypeReference*> ResolvedTypeReferenceMap; +// While this could be a hash, a map is chosen here to provide a stable +// order, which is used to calculating a check-sum on dependent meta-objects. +struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*> +{ + bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const; +}; +#else +struct ResolvedTypeReferenceMap {}; #endif // index is per-object binding index diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 768a4ffcd3..924f2e15e2 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -43,6 +43,7 @@ #include <private/qv4string_p.h> #include <private/qv4value_p.h> #include <private/qv4alloca_p.h> +#include <wtf/MathExtras.h> QV4::Compiler::StringTableGenerator::StringTableGenerator() { @@ -361,6 +362,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.qtVersion = QT_VERSION; unit.architectureIndex = registerString(QSysInfo::buildAbi()); unit.codeGeneratorIndex = registerString(codeGeneratorName); + memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); quint32 nextOffset = sizeof(CompiledData::Unit); @@ -377,6 +379,9 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp nextOffset += unit.regexpTableSize * sizeof(CompiledData::RegExp); unit.constantTableSize = constants.size(); + + // Ensure we load constants from well-aligned addresses into for example SSE registers. + nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(16, nextOffset)); unit.offsetToConstantTable = nextOffset; nextOffset += unit.constantTableSize * sizeof(ReturnedValue); diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 08e4f0a8c0..625f5b5e5a 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -148,8 +148,7 @@ bool CompilationUnit::memoryMapCode(QString *errorString) const Assembler::VoidType Assembler::Void; Assembler::Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) - : _constTable(this) - , _function(function) + : _function(function) , _nextBlock(0) , _executableAllocator(executableAllocator) , _isel(isel) @@ -279,6 +278,19 @@ Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &s return Pointer(reg, id * sizeof(QV4::String*)); } +Assembler::Address Assembler::loadConstant(IR::Const *c, RegisterID baseReg) +{ + return loadConstant(convertToValue(c), baseReg); +} + +Assembler::Address Assembler::loadConstant(const Primitive &v, RegisterID baseReg) +{ + loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), baseReg); + loadPtr(Address(baseReg, qOffsetOf(QV4::Heap::ExecutionContext, constantTable)), baseReg); + const int index = _isel->jsUnitGenerator()->registerConstant(v.asReturnedValue()); + return Address(baseReg, index * sizeof(QV4::Value)); +} + void Assembler::loadStringRef(RegisterID reg, const QString &string) { const int id = _isel->registerString(string); diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 669b0b2ff4..d76a21c74c 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -89,7 +89,6 @@ struct CompilationUnit : public QV4::CompiledData::CompilationUnit // Coderef + execution engine QVector<JSC::MacroAssemblerCodeRef> codeRefs; - QList<QVector<QV4::Primitive> > constantValues; }; struct LookupCall { @@ -297,22 +296,6 @@ public: int savedRegCount; }; - class ConstantTable - { - public: - ConstantTable(Assembler *as): _as(as) {} - - int add(const QV4::Primitive &v); - Address loadValueAddress(IR::Const *c, RegisterID baseReg); - Address loadValueAddress(const QV4::Primitive &v, RegisterID baseReg); - void finalize(JSC::LinkBuffer &linkBuffer, InstructionSelection *isel); - - private: - Assembler *_as; - QVector<QV4::Primitive> _values; - QVector<DataLabelPtr> _toPatch; - }; - struct VoidType { VoidType() {} }; static const VoidType Void; @@ -382,6 +365,8 @@ public: Pointer loadTempAddress(IR::Temp *t); Pointer loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al); Pointer loadStringAddress(RegisterID reg, const QString &string); + Address loadConstant(IR::Const *c, RegisterID baseReg); + Address loadConstant(const Primitive &v, RegisterID baseReg); void loadStringRef(RegisterID reg, const QString &string); Pointer stackSlotPointer(IR::Temp *t) const { @@ -1029,7 +1014,7 @@ public: move(TrustedImm64(i), ReturnValueRegister); move64ToDouble(ReturnValueRegister, target); #else - JSC::MacroAssembler::loadDouble(constantTable().loadValueAddress(c, ScratchRegister), target); + JSC::MacroAssembler::loadDouble(loadConstant(c, ScratchRegister), target); #endif return target; } @@ -1093,7 +1078,7 @@ public: // it's not in signed int range, so load it as a double, and truncate it down loadDouble(addr, FPGpr0); - Address inversionAddress = constantTable().loadValueAddress(QV4::Primitive::fromDouble(double(INT_MAX) + 1), scratchReg); + Address inversionAddress = loadConstant(QV4::Primitive::fromDouble(double(INT_MAX) + 1), scratchReg); subDouble(inversionAddress, FPGpr0); Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg); canNeverHappen.link(this); @@ -1111,14 +1096,12 @@ public: void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave); const StackLayout &stackLayout() const { return *_stackLayout.data(); } - ConstantTable &constantTable() { return _constTable; } Label exceptionReturnLabel; IR::BasicBlock * catchBlock; QVector<Jump> exceptionPropagationJumps; private: QScopedPointer<const StackLayout> _stackLayout; - ConstantTable _constTable; IR::Function *_function; QHash<IR::BasicBlock *, Label> _addrs; QHash<IR::BasicBlock *, QVector<Jump> > _patches; diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index c09fc6fdca..45cc9259c3 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -162,7 +162,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) #if CPU(X86) if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address] as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister); + Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister); as->addDouble(addr, targetReg); break; } @@ -184,7 +184,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) #if CPU(X86) if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address] as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister); + Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister); as->mulDouble(addr, targetReg); break; } @@ -203,7 +203,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) #if CPU(X86) if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address] as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister); + Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister); as->subDouble(addr, targetReg); break; } @@ -231,7 +231,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) #if CPU(X86) if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address] as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister); + Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister); as->divDouble(addr, targetReg); break; } diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 4066ab213c..da28df817d 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -177,7 +177,6 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) linkBuffer.patch(label, linkBuffer.locationOf(target)); } } - _constTable.finalize(linkBuffer, _isel); *codeSize = linkBuffer.offsetOf(endOfCode); @@ -370,16 +369,6 @@ void InstructionSelection::run(int functionIndex) qSwap(_removableJumps, removableJumps); } -const void *InstructionSelection::addConstantTable(QVector<Primitive> *values) -{ - compilationUnit->constantValues.append(*values); - values->clear(); - - QVector<QV4::Primitive> &finalValues = compilationUnit->constantValues.last(); - finalValues.squeeze(); - return finalValues.constData(); -} - QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep() { QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; @@ -1719,38 +1708,6 @@ bool operator==(const Primitive &v1, const Primitive &v2) } // QV4 namespace QT_END_NAMESPACE -int Assembler::ConstantTable::add(const Primitive &v) -{ - int idx = _values.indexOf(v); - if (idx == -1) { - idx = _values.size(); - _values.append(v); - } - return idx; -} - -Assembler::Address Assembler::ConstantTable::loadValueAddress(IR::Const *c, RegisterID baseReg) -{ - return loadValueAddress(convertToValue(c), baseReg); -} - -Assembler::Address Assembler::ConstantTable::loadValueAddress(const Primitive &v, RegisterID baseReg) -{ - _toPatch.append(_as->moveWithPatch(TrustedImmPtr(0), baseReg)); - Address addr(baseReg); - addr.offset = add(v) * sizeof(QV4::Primitive); - Q_ASSERT(addr.offset >= 0); - return addr; -} - -void Assembler::ConstantTable::finalize(JSC::LinkBuffer &linkBuffer, InstructionSelection *isel) -{ - const void *tablePtr = isel->addConstantTable(&_values); - - foreach (DataLabelPtr label, _toPatch) - linkBuffer.patch(label, const_cast<void *>(tablePtr)); -} - bool InstructionSelection::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) { diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 4b35a72e01..93453f71be 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -81,7 +81,6 @@ public: virtual void run(int functionIndex); - const void *addConstantTable(QVector<QV4::Primitive> *values); protected: virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> backendCompileStep(); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 97b3e26a26..60717c9491 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -73,6 +73,7 @@ Heap::CallContext *ExecutionContext::newCallContext(const FunctionObject *functi c->compilationUnit = function->function()->compilationUnit; c->lookups = c->compilationUnit->runtimeLookups; + c->constantTable = c->compilationUnit->data->constants(); c->locals = (Value *)((quintptr(c + 1) + 7) & ~7); const CompiledData::Function *compiledFunction = function->function()->compiledFunction; @@ -172,6 +173,7 @@ Heap::WithContext::WithContext(ExecutionContext *outerContext, Object *with) outer = outerContext; callData = outer->callData; lookups = outer->lookups; + constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; withObject = with; @@ -184,6 +186,7 @@ Heap::CatchContext::CatchContext(ExecutionContext *outerContext, String *excepti strictMode = outer->strictMode; callData = outer->callData; lookups = outer->lookups; + constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; this->exceptionVarName = exceptionVarName; @@ -197,6 +200,7 @@ Heap::QmlContext::QmlContext(QV4::ExecutionContext *outerContext, QV4::QmlContex strictMode = false; callData = outer->callData; lookups = outer->lookups; + constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; this->qml = qml->d(); diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 2e6773a927..368605ca4a 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -108,6 +108,7 @@ struct ExecutionContext : Base { ExecutionEngine *engine; Pointer<ExecutionContext> outer; Lookup *lookups; + const QV4::Value *constantTable; CompiledData::CompilationUnit *compilationUnit; ContextType type : 8; @@ -118,9 +119,10 @@ struct ExecutionContext : Base { inline ExecutionContext::ExecutionContext(ExecutionEngine *engine, ContextType t) : engine(engine) - , outer(0) - , lookups(0) - , compilationUnit(0) + , outer(nullptr) + , lookups(nullptr) + , constantTable(nullptr) + , compilationUnit(nullptr) , type(t) , strictMode(false) , lineNumber(-1) diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 6b9c552350..805087e389 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -535,6 +535,7 @@ void SimpleScriptFunction::construct(const Managed *that, Scope &scope, CallData ctx.function = f->d(); ctx.compilationUnit = f->function()->compilationUnit; ctx.lookups = ctx.compilationUnit->runtimeLookups; + ctx.constantTable = ctx.compilationUnit->data->constants(); ctx.outer = f->scope(); ctx.locals = scope.alloc(f->varCount()); for (int i = callData->argc; i < (int)f->formalParameterCount(); ++i) @@ -572,6 +573,7 @@ void SimpleScriptFunction::call(const Managed *that, Scope &scope, CallData *cal ctx.function = f->d(); ctx.compilationUnit = f->function()->compilationUnit; ctx.lookups = ctx.compilationUnit->runtimeLookups; + ctx.constantTable = ctx.compilationUnit->data->constants(); ctx.outer = f->scope(); ctx.locals = scope.alloc(f->varCount()); for (int i = callData->argc; i < (int)f->formalParameterCount(); ++i) diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index a2e379ec1a..46adaf7e79 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -222,6 +222,7 @@ ReturnedValue Script::run() ContextStateSaver stateSaver(valueScope, scope); scope->d()->strictMode = vmFunction->isStrict(); scope->d()->lookups = vmFunction->compilationUnit->runtimeLookups; + scope->d()->constantTable = vmFunction->compilationUnit->data->constants(); scope->d()->compilationUnit = vmFunction->compilationUnit; return Q_V4_PROFILE(engine, vmFunction); diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index e81bc3049c..2e87a7692b 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -71,6 +71,7 @@ struct ContextStateSaver { Value *savedContext; bool strictMode; Lookup *lookups; + const QV4::Value *constantTable; CompiledData::CompilationUnit *compilationUnit; int lineNumber; @@ -78,6 +79,7 @@ struct ContextStateSaver { : savedContext(scope.alloc(1)) , strictMode(context->d()->strictMode) , lookups(context->d()->lookups) + , constantTable(context->d()->constantTable) , compilationUnit(context->d()->compilationUnit) , lineNumber(context->d()->lineNumber) { @@ -87,6 +89,7 @@ struct ContextStateSaver { : savedContext(scope.alloc(1)) , strictMode(context->strictMode) , lookups(context->lookups) + , constantTable(context->constantTable) , compilationUnit(context->compilationUnit) , lineNumber(context->lineNumber) { @@ -98,6 +101,7 @@ struct ContextStateSaver { Heap::ExecutionContext *ctx = static_cast<Heap::ExecutionContext *>(savedContext->m()); ctx->strictMode = strictMode; ctx->lookups = lookups; + ctx->constantTable = constantTable; ctx->compilationUnit = compilationUnit; ctx->lineNumber = lineNumber; } diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 10d16a8a12..ffe1b4287d 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -51,6 +51,8 @@ #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlvaluetypewrapper_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qv4variantobject_p.h> #include <QVariant> #include <QtCore/qdebug.h> @@ -59,7 +61,7 @@ QT_BEGIN_NAMESPACE QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContext *ctxt) { - QQmlBinding *b = newBinding(property); + QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); b->setNotifyOnValueChanged(true); b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt)); b->setScopeObject(obj); @@ -71,7 +73,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt) { - QQmlBinding *b = newBinding(property); + QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); if (ctxt && !ctxt->isValid()) return b; @@ -108,7 +110,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContextData *ctxt) { - QQmlBinding *b = newBinding(property); + QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); b->setNotifyOnValueChanged(true); b->QQmlJavaScriptExpression::setContext(ctxt); @@ -123,7 +125,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString QQmlContextData *ctxt, const QString &url, quint16 lineNumber, quint16 columnNumber) { - QQmlBinding *b = newBinding(property); + QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); Q_UNUSED(columnNumber); b->setNotifyOnValueChanged(true); @@ -137,7 +139,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt) { - QQmlBinding *b = newBinding(property); + QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); b->setNotifyOnValueChanged(true); b->QQmlJavaScriptExpression::setContext(ctxt); @@ -204,18 +206,13 @@ protected: const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL { QQmlPropertyData pd = getPropertyData(); - - int idx = pd.coreIndex; - Q_ASSERT(idx != -1); - - int status = -1; - void *a[] = { &binding, 0, &status, &flags }; - QMetaObject::metacall(*m_target, QMetaObject::WriteProperty, idx, a); + pd.writeProperty(*m_target, &binding, flags); } }; -template<int StaticPropType> -class GenericBinding: public QQmlBinding +// For any target that's not a binding, we have a common doUpdate. However, depending on the type +// of the target property, there is a specialized write method. +class QQmlNonbindingBinding: public QQmlBinding { protected: void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, @@ -252,9 +249,16 @@ protected: ep->dereferenceScarceResources(); } + virtual bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) = 0; +}; + +template<int StaticPropType> +class GenericBinding: public QQmlNonbindingBinding +{ +protected: // Returns true if successful, false if an error description was set on expression Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, - QQmlPropertyData::WriteFlags flags) + QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL { QQmlPropertyData pd = getPropertyData(); int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded. @@ -572,8 +576,68 @@ Q_ALWAYS_INLINE int QQmlBinding::getPropertyCoreIndex() const } } -QQmlBinding *QQmlBinding::newBinding(const QQmlPropertyData *property) +class QObjectPointerBinding: public QQmlNonbindingBinding { + QQmlMetaObject targetMetaObject; + +public: + QObjectPointerBinding(QQmlEnginePrivate *engine, int propertyType) + : targetMetaObject(QQmlPropertyPrivate::rawMetaObjectForType(engine, propertyType)) + {} + +protected: + Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, + QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL + { + QQmlPropertyData pd = getPropertyData(); + if (Q_UNLIKELY(isUndefined || pd.isValueTypeVirtual())) + return slowWrite(pd, result, isUndefined, flags); + + // Check if the result is a QObject: + QObject *resultObject = nullptr; + QQmlMetaObject resultMo; + if (result.isNull()) { + // Special case: we can always write a nullptr. Don't bother checking anything else. + return pd.writeProperty(targetObject(), &resultObject, flags); + } else if (auto wrapper = result.as<QV4::QObjectWrapper>()) { + resultObject = wrapper->object(); + if (!resultObject) + return pd.writeProperty(targetObject(), &resultObject, flags); + if (QQmlData *ddata = QQmlData::get(resultObject, false)) + resultMo = ddata->propertyCache; + if (resultMo.isNull()) { + resultMo = resultObject->metaObject(); + } + } else if (auto variant = result.as<QV4::VariantObject>()) { + QVariant value = variant->d()->data; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()); + resultMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, value.userType()); + if (resultMo.isNull()) + return slowWrite(pd, result, isUndefined, flags); + resultObject = *static_cast<QObject *const *>(value.constData()); + } else { + return slowWrite(pd, result, isUndefined, flags); + } + + // Compare & set: + if (QQmlMetaObject::canConvert(resultMo, targetMetaObject)) { + return pd.writeProperty(targetObject(), &resultObject, flags); + } else if (!resultObject && QQmlMetaObject::canConvert(targetMetaObject, resultMo)) { + // In the case of a null QObject, we assign the null if there is + // any change that the null variant type could be up or down cast to + // the property type. + return pd.writeProperty(targetObject(), &resultObject, flags); + } else { + return slowWrite(pd, result, isUndefined, flags); + } + } +}; + +QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property) +{ + if (property && property->isQObject()) + return new QObjectPointerBinding(engine, property->propType); + const int type = (property && property->isFullyResolved()) ? property->propType : QMetaType::UnknownType; if (type == qMetaTypeId<QQmlBinding *>()) { diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 0c797d7df2..67fbeb693e 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -119,7 +119,7 @@ private: inline bool enabledFlag() const; inline void setEnabledFlag(bool); - static QQmlBinding *newBinding(const QQmlPropertyData *property); + static QQmlBinding *newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property); }; bool QQmlBinding::updatingFlag() const diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index c54af43a6f..5bc9c8ac25 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -62,6 +62,7 @@ #include <QtCore/qdiriterator.h> #include <QtQml/qqmlcomponent.h> #include <QtCore/qwaitcondition.h> +#include <QtCore/qloggingcategory.h> #include <QtQml/qqmlextensioninterface.h> #include <functional> @@ -105,6 +106,9 @@ DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); DEFINE_BOOL_CONFIG_OPTION(diskCache, QML_DISK_CACHE); DEFINE_BOOL_CONFIG_OPTION(forceDiskCacheRefresh, QML_FORCE_DISK_CACHE_REFRESH); +Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) +Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache") + QT_BEGIN_NAMESPACE namespace { @@ -2075,7 +2079,7 @@ bool QQmlTypeData::tryLoadFromDiskCache() { QString error; if (!unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { - qDebug() << "Error loading" << url().toString() << "from disk cache:" << error; + qCDebug(DBG_DISK_CACHE) << "Error loading" << url().toString() << "from disk cache:" << error; return false; } } @@ -2485,7 +2489,7 @@ void QQmlTypeData::compile() if (diskCache() || forceDiskCacheRefresh()) { QString errorString; if (!m_compiledData->saveToDisk(&errorString)) { - qDebug() << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString; + qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString; } } } @@ -2877,7 +2881,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) initializeFromCompilationUnit(unit); return; } else { - qDebug() << "Error loading" << url().toString() << "from disk cache:" << error; + qCDebug(DBG_DISK_CACHE()) << "Error loading" << url().toString() << "from disk cache:" << error; } } @@ -2910,7 +2914,8 @@ void QQmlScriptBlob::dataReceived(const Data &data) irUnit.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary; QmlIR::QmlUnitGenerator qmlGenerator; - QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit); + QV4::CompiledData::ResolvedTypeReferenceMap emptyDependencies; + QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit, m_typeLoader->engine(), emptyDependencies); Q_ASSERT(!unit->data); // The js unit owns the data and will free the qml unit. unit->data = unitData; @@ -2918,7 +2923,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) if (diskCache() || forceDiskCacheRefresh()) { QString errorString; if (!unit->saveToDisk(&errorString)) { - qDebug() << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString; + qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString; } } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index ab6b046fcf..5f754df1fc 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -463,7 +463,9 @@ private: QList<TypeReference> m_compositeSingletons; // map from name index to resolved type - QHash<int, TypeReference> m_resolvedTypes; + // While this could be a hash, a map is chosen here to provide a stable + // order, which is used to calculating a check-sum on dependent meta-objects. + QMap<int, TypeReference> m_resolvedTypes; bool m_typesResolved:1; QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compiledData; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 032a06f946..d900688173 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -187,6 +187,12 @@ void QSGSoftwareRenderableNode::update() boundingRect = m_handle.spriteNode->rect().toRect(); break; case QSGSoftwareRenderableNode::RenderNode: + if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering) && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.renderNode->rect().toRect(); break; default: break; @@ -244,14 +250,20 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu rd->m_opacity = m_opacity; RenderNodeState rs; rs.cr = m_clipRegion; + + const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering) + ? m_boundingRect : + QRect(0, 0, painter->device()->width(), painter->device()->height()); + painter->save(); + painter->setClipRegion(br, Qt::ReplaceClip); m_handle.renderNode->render(&rs); painter->restore(); - const QRect fullRect = QRect(0, 0, painter->device()->width(), painter->device()->height()); - m_previousDirtyRegion = fullRect; + + m_previousDirtyRegion = QRegion(br); m_isDirty = false; m_dirtyRegion = QRegion(); - return fullRect; + return br; } } diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index bee2015007..49bbbf0ba8 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -1033,11 +1033,13 @@ void Renderer::nodeWasAdded(QSGNode *node, Node *shadowParent) m_rebuild |= FullRebuild; } else if (node->type() == QSGNode::RenderNodeType) { - RenderNodeElement *e = new RenderNodeElement(static_cast<QSGRenderNode *>(node)); + QSGRenderNode *rn = static_cast<QSGRenderNode *>(node); + RenderNodeElement *e = new RenderNodeElement(rn); snode->data = e; - Q_ASSERT(!m_renderNodeElements.contains(static_cast<QSGRenderNode *>(node))); + Q_ASSERT(!m_renderNodeElements.contains(rn)); m_renderNodeElements.insert(e->renderNode, e); - m_useDepthBuffer = false; + if (!rn->flags().testFlag(QSGRenderNode::DepthAwareRendering)) + m_useDepthBuffer = false; m_rebuild |= FullRebuild; } diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 365abd09e2..5915d51f2b 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -179,6 +179,11 @@ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const default value according to the OpenGL specification. For other APIs, see the documentation for changedStates() for more information. + \note Depth writes are disabled when this function is called (for example, + glDepthMask(false) in case of OpenGL). Enabling depth writes can lead to + unexpected results, depending on the scenegraph backend in use, so nodes + should avoid this. + For APIs other than OpenGL, it will likely be necessary to query certain API-specific resources (for example, the graphics device or the command list/buffer to add the commands to). This is done via QSGRendererInterface. @@ -211,6 +216,68 @@ void QSGRenderNode::releaseResources() } /*! + \enum QSGRenderNode::RenderingFlag + + Possible values for the bitmask returned from flags(). + + \value BoundedRectRendering Indicates that the implementation of render() + does not render outside the area reported from rect() in item + coordinates. Such node implementations can lead to more efficient rendering, + depending on the scenegraph backend. For example, the software backend can + continue to use the more optimal partial update path when all render nodes + in the scene have this flag set. + + \value DepthAwareRendering Indicates that the implementations of render() + conforms to scenegraph expectations by only generating a Z value of 0 in + scene coordinates which is then transformed by the matrices retrieved from + RenderState::projectionMatrix() and matrix(), as described in the notes for + render(). Such node implementations can lead to more efficient rendering, + depending on the scenegraph backend. For example, the batching OpenGL + renderer can continue to use a more optimal path when all render nodes in + the scene have this flag set. + + \value OpaqueRendering Indicates that the implementation of render() writes + out opaque pixels for the entire area reported from rect(). By default the + renderers must assume that render() can also output semi or fully + transparent pixels. Setting this flag can improve performance in some + cases. + + \sa render(), rect() + */ + +/*! + \return flags describing the behavior of this render node. + + The default implementation returns 0. + + \sa RenderingFlag, rect() + */ +QSGRenderNode::RenderingFlags QSGRenderNode::flags() const +{ + return 0; +} + +/*! + \return the bounding rectangle in item coordinates for the area render() + touches. The value is only in use when flags() includes + BoundedRectRendering, ignored otherwise. + + Reporting the rectangle in combination with BoundedRectRendering is + particularly important with the \c software backend because otherwise + having a rendernode in the scene would trigger fullscreen updates, skipping + all partial update optimizations. + + For rendernodes covering the entire area of a corresponding QQuickItem the + return value will be (0, 0, item->width(), item->height()). + + \sa flags() +*/ +QRectF QSGRenderNode::rect() const +{ + return QRectF(); +} + +/*! \return pointer to the current model-view matrix. */ const QMatrix4x4 *QSGRenderNode::matrix() const diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.h b/src/quick/scenegraph/coreapi/qsgrendernode.h index 6eb425c03b..f6bc40d3ee 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.h +++ b/src/quick/scenegraph/coreapi/qsgrendernode.h @@ -61,6 +61,13 @@ public: }; Q_DECLARE_FLAGS(StateFlags, StateFlag) + enum RenderingFlag { + BoundedRectRendering = 0x01, + DepthAwareRendering = 0x02, + OpaqueRendering = 0x04 + }; + Q_DECLARE_FLAGS(RenderingFlags, RenderingFlag) + struct Q_QUICK_EXPORT RenderState { virtual ~RenderState(); virtual const QMatrix4x4 *projectionMatrix() const = 0; @@ -78,6 +85,8 @@ public: virtual StateFlags changedStates() const; virtual void render(const RenderState *state) = 0; virtual void releaseResources(); + virtual RenderingFlags flags() const; + virtual QRectF rect() const; const QMatrix4x4 *matrix() const; const QSGClipNode *clipList() const; @@ -89,6 +98,7 @@ private: }; Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::RenderingFlags) QT_END_NAMESPACE |